#62921 - Provide OOXMLLite alternative for Java 12+

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1846809 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2018-11-18 00:01:40 +00:00
parent 0bb47ba1e4
commit 42af181065
8 changed files with 223 additions and 553 deletions

147
build.xml
View File

@ -130,8 +130,12 @@ under the License.
<property name="ooxml.output.dir" location="build/ooxml-classes"/>
<property name="ooxml.output.test.dir" location="build/ooxml-test-classes"/>
<property name="ooxml.testokfile" location="build/ooxml-testokfile.txt"/>
<property name="ooxml.lite.output.dir" location="build/ooxml-lite-classes"/>
<property name="ooxml.lite.testokfile" location="build/ooxml-lite-testokfile.txt"/>
<property name="ooxml.lite.agent" location="build/ooxml-lite-agent.jar"/>
<property name="ooxml.lite.report" location="build/ooxml-lite-report.txt"/>
<property name="ooxml.lite.jar" location="build/ooxml-lite-classes.jar"/>
<property name="ooxml.lite.includes" value="^(com/microsoft/schemas|org/(etsi|openxmlformats|w3/)|schemaorg_apache_xmlbeans)"/>
<!-- Integration testing: -->
<property name="integration.src.test" location="src/integrationtest"/>
@ -397,7 +401,8 @@ under the License.
<path id="ooxml-lite.classpath">
<path refid="ooxml.base.classpath"/>
<pathelement location="${ooxml.lite.output.dir}"/> <!-- instead of ooxml-xsds.jar use the filtered classes-->
<!-- instead of ooxml-xsds.jar use the filtered classes-->
<pathelement location="${ooxml.lite.jar}"/>
<pathelement location="${ooxml.output.dir}"/>
<pathelement location="${ooxml.output.test.dir}"/>
<pathelement location="${main.output.test.dir}"/>
@ -1015,8 +1020,6 @@ under the License.
compile-scratchpad, compile-examples, compile-excelant"
description="Compiles the POI main classes, scratchpad and examples"/>
<target name="compile-all" depends="compile,compile-ooxml-lite"/>
<target name="compile-main" depends="init">
<!-- compile the sources -->
<javac target="${jdk.version.class}"
@ -1338,7 +1341,7 @@ under the License.
<!-- Section: test (execute junit tests on test suites) -->
<target name="test" depends="compile,jacocotask,test-main,test-scratchpad,test-ooxml,test-excelant"
description="Tests main, scratchpad and ooxml"/>
<target name="test-all" depends="test,test-ooxml-lite,testcoveragereport"
<target name="test-all" depends="test,test-integration,test-ooxml-lite,testcoveragereport"
description="Tests main, scratchpad, ooxml, ooxml-lite, and coveragereport"/>
<target name="testcoveragereport" depends="init,jacocotask" description="create test-report" xmlns:jacoco="antlib:org.jacoco.ant" if="coverage.enabled">
@ -1385,9 +1388,7 @@ under the License.
<group name="OOXML">
<classfiles>
<fileset dir="${ooxml.output.dir}"/>
<!-- there are no actual POI classes in build/ooxml-lite-classes, only generated code...
<fileset dir="${ooxml.lite.output.dir}"/>
-->
<!-- there are no actual POI classes in build/ooxml-lite-classes, only generated code... -->
</classfiles>
<sourcefiles encoding="UTF-8">
<fileset dir="${ooxml.src}"/>
@ -1564,6 +1565,7 @@ under the License.
<sysproperty key="java.io.tmpdir" value="${tempdir}"/>
<jvmarg value="-Xmx768M"/>
<jvmarg value="-ea"/>
<jvmarg value="-javaagent:${ooxml.lite.agent}=${ooxml.lite.report}|${ooxml.lite.includes}"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<jvmarg value="${java9addopens1}" />
@ -1598,6 +1600,7 @@ under the License.
</classpath>
<syspropertyset refid="junit.properties"/>
<sysproperty key="java.io.tmpdir" value="${tempdir}"/>
<jvmarg value="-javaagent:${ooxml.lite.agent}=${ooxml.lite.report}|${ooxml.lite.includes}"/>
<jvmarg value="-Xmx768M"/>
<jvmarg value="-ea"/>
<jvmarg value="${java9addmods}" />
@ -1620,7 +1623,7 @@ under the License.
</sequential>
</macrodef>
<target name="test-ooxml" depends="compile-main,compile-ooxml,-test-ooxml-check,jacocotask" unless="ooxml.test.notRequired"
<target name="test-ooxml" depends="compile-main,compile-ooxml,-test-ooxml-check,jacocotask,-ooxml-lite-agent" unless="ooxml.test.notRequired"
description="test OOXML classes">
<ooxml-test-runner classpath="test.ooxml.classpath" type="ooxml"/>
<delete file="${ooxml.testokfile}"/>
@ -1638,7 +1641,7 @@ under the License.
</uptodate>
</target>
<target name="test-integration" depends="compile-integration,-test-integration-check,jacocotask"
<target name="test-integration" depends="compile-integration,-test-integration-check,jacocotask,-ooxml-lite-agent"
unless="integration.test.notRequired" xmlns:jacoco="antlib:org.jacoco.ant">
<propertyreset name="org.apache.poi.util.POILogger" value="org.apache.poi.util.CommonsLogger"/>
<delete dir="build" includes="test-integration.log*"/>
@ -1650,6 +1653,7 @@ under the License.
<sysproperty key="java.io.tmpdir" value="${tempdir}"/>
<jvmarg value="-ea"/>
<jvmarg value="-Xmx1512M"/>
<jvmarg value="-javaagent:${ooxml.lite.agent}=${ooxml.lite.report}|${ooxml.lite.includes}"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<jvmarg value="${java9addopens1}" />
@ -1677,57 +1681,28 @@ under the License.
<echo file="${integration.testokfile}" append="false" message="testok"/>
</target>
<!-- Section: test-ooxml-lite -->
<target name="-compile-ooxml-lite-check">
<uptodate property="ooxml.lite.test.notRequired" targetfile="${ooxml.lite.testokfile}">
<srcfiles dir="${ooxml.src}"/>
<srcfiles dir="${ooxml.src.test}"/>
<srcfiles file="${ooxml.xsds.jar}"/>
<srcfiles file="${ooxml.security.jar}"/>
</uptodate>
</target>
<!-- the ooxml lite agent collects referenced schema files while other junit tests are run, -->
<!-- therefore its best to compile the ooxml lite jar after all usual tests are done -->
<target name="compile-ooxml-lite" depends="compile-ooxml,-ooxml-lite-agent,test-ooxml">
<echo message="Create ooxml-lite schemas"/>
<target name="compile-ooxml-lite" depends="-compile-ooxml-lite-check,compile-ooxml"
unless="ooxml.lite.test.notRequired">
<delete file="${ooxml.lite.testokfile}"/>
<echo message="Running ooxml-lite generator"/>
<copy file="${ooxml.lite.report}" tofile="${ooxml.lite.report}-pat" overwrite="true"/>
<replaceregexp file="${ooxml.lite.report}-pat" byline="true" match="(.*)" replace="\1.class${line.separator}\1$*.class"/>
<property name="ooxml.lite-merged.dir" location="build/ooxml-lite-merged"/>
<mkdir dir="${ooxml.lite-merged.dir}"/>
<patternset id="xsbfiles">
<includesfile name="${ooxml.lite.report}-pat"/>
<include name="schemaorg_apache_xmlbeans/system/**/*.xsb"/>
<include name="schemaorg_apache_xmlbeans/element/**/*.xsb"/>
</patternset>
<jar destfile="${ooxml.lite-merged.dir}/ooxml-lite-merged.jar">
<zipfileset includes="**/*" src="${ooxml.xsds.jar}"/>
<zipfileset includes="**/*" src="${ooxml.security.jar}"/>
<jar destfile="${ooxml.lite.jar}" duplicate="preserve">
<zipfileset src="${ooxml.xsds.jar}">
<patternset refid="xsbfiles"/>
</zipfileset>
<zipfileset src="${ooxml.security.jar}">
<patternset refid="xsbfiles"/>
</zipfileset>
</jar>
<java classname="org.apache.poi.ooxml.util.OOXMLLite" fork="yes"
failonerror="true">
<classpath>
<pathelement path="${ooxml.lite-merged.dir}/ooxml-lite-merged.jar"/>
</classpath>
<classpath refid="test.ooxml.classpath"/>
<classpath refid="ooxml.xmlsec.classpath"/>
<syspropertyset refid="junit.properties"/>
<sysproperty key="java.io.tmpdir" value="${tempdir}"/>
<jvmarg value="-Xmx512m"/>
<jvmarg value="${java9addmods}" />
<jvmarg value="${java9addmodsvalue}" />
<jvmarg value="${java9addopens1}" />
<jvmarg value="${java9addopens2}" />
<jvmarg value="${java9addopens3}" />
<jvmarg value="${java9addopens4}" />
<jvmarg value="${java9addopens5}" />
<jvmarg value="${java9addopens6}" />
<jvmarg value="${file.leak.detector}" />
<arg value="-ooxml"/>
<arg value="${ooxml.lite-merged.dir}/ooxml-lite-merged.jar"/>
<arg value="-test"/>
<arg value="${ooxml.output.test.dir}"/>
<arg value="-dest"/>
<arg value="${ooxml.lite.output.dir}"/>
</java>
<echo file="${ooxml.lite.testokfile}" append="false" message="testok"/>
</target>
<target name="test-ooxml-lite" depends="jacocotask,compile-ooxml-xsds,compile-ooxml-lite">
@ -1736,6 +1711,15 @@ under the License.
<ooxml-test-runner classpath="ooxml-lite.classpath" type="ooxml-lite"/>
</target>
<target name="-ooxml-lite-agent" depends="jacocotask,compile-ooxml-xsds,compile-ooxml">
<jar destfile="${ooxml.lite.agent}">
<fileset dir="${ooxml.output.test.dir}" includes="org/apache/poi/ooxml/util/OOXMLLiteAgent*.class"/>
<manifest>
<attribute name="Premain-Class" value="org.apache.poi.ooxml.util.OOXMLLiteAgent"/>
</manifest>
</jar>
</target>
<!-- Section: test-excelant -->
<target name="-test-excelant-check">
<uptodate property="excelant.test.notRequired" targetfile="${excelant.testokfile}">
@ -1953,11 +1937,11 @@ under the License.
</target>
<macrodef name="maven-jar">
<attribute name="dir"/>
<attribute name="src"/>
<sequential>
<local name="destfile"/>
<pathconvert property="destfile" targetos="unix">
<path><pathelement path="@{dir}"/></path>
<path><pathelement path="@{src}"/></path>
<mapper>
<chainedmapper>
<filtermapper>
@ -1971,33 +1955,39 @@ under the License.
</chainedmapper>
</mapper>
</pathconvert>
<local name="isjar"/>
<condition property="isjar">
<contains string="@{src}" substring=".jar"/>
</condition>
<jar destfile="build/dist/maven/${destfile}.jar"
manifest="build/poi-manifest.mf">
<fileset dir="@{dir}"/>
<fileset dir="@{src}" unless:true="${isjar}"/>
<zipfileset src="@{src}" if:true="${isjar}"/>
<metainf dir="legal/"/>
</jar>
</sequential>
</macrodef>
<target name="jar" depends="compile-all, compile-version, -manifest" description="Creates jar files for distribution">
<maven-jar dir="${main.output.dir}"/>
<maven-jar dir="${scratchpad.output.dir}"/>
<maven-jar dir="${ooxml.output.dir}"/>
<maven-jar dir="${examples.output.dir}"/>
<maven-jar dir="${excelant.output.dir}"/>
<maven-jar dir="${ooxml.lite.output.dir}"/>
<target name="jar" depends="compile, compile-version, -manifest" description="Creates jar files for distribution">
<maven-jar src="${main.output.dir}"/>
<maven-jar src="${scratchpad.output.dir}"/>
<maven-jar src="${ooxml.output.dir}"/>
<maven-jar src="${examples.output.dir}"/>
<maven-jar src="${excelant.output.dir}"/>
<maven-jar src="${ooxml.lite.jar}"/>
</target>
<target name="jar-src" depends="compile-all, compile-version, -manifest" description="Sources for Maven">
<maven-jar dir="${main.src}"/>
<maven-jar dir="${scratchpad.src}"/>
<maven-jar dir="${ooxml.src}"/>
<maven-jar dir="${examples.src}"/>
<maven-jar dir="${excelant.src}"/>
<target name="jar-src" depends="compile, compile-version, -manifest" description="Sources for Maven">
<maven-jar src="${main.src}"/>
<maven-jar src="${scratchpad.src}"/>
<maven-jar src="${ooxml.src}"/>
<maven-jar src="${examples.src}"/>
<maven-jar src="${excelant.src}"/>
</target>
<target name="integration-test-jar" depends="compile-integration,-manifest" description="target for packaging the integration-test code for mass regression test runs">
<maven-jar dir="${integration.output.test.dir}"/>
<maven-jar src="${integration.output.test.dir}"/>
</target>
<target name="-do-jar-check-javadocs-package-list">
@ -2112,6 +2102,7 @@ under the License.
lib/**,
bin/**,
out/**,
tmp/**,
sonar/**/target/**,
sonar/*/src/**,
compile-lib/**,
@ -2167,14 +2158,12 @@ under the License.
</mvn:mvn>
</target>
<target name="dist" depends="clean, compile-all, test-all, rat-check, forbidden-apis-check, docs, jar, release-notes, assemble"
<target name="dist" depends="clean, compile, test-all, rat-check, forbidden-apis-check, docs, jar, release-notes, assemble"
description="Creates the entire distribution into build/dist, from scratch">
</target>
<!-- continuous integration targets -->
<target name="gump" depends="compile-all, test-all, jar"
description="Target for running with Apache Gump continuous integration. Builds and tests POI and generates jar artifacts." />
<target name="jenkins" depends="compile-all, test-all, test-integration, jar, javadocs, assemble, findbugs, release-notes, rat-check, forbidden-apis-check"
<target name="jenkins" depends="compile, test-all, jar, javadocs, assemble, findbugs, release-notes, rat-check, forbidden-apis-check"
description="Target run by Jenkins on a continuous basis. Builds and tests POI, generates artifacts and documentation, and searches for problems."/>
<available property="maven.ant.tasks.present" classname="org.apache.maven.artifact.ant.Pom"/>

View File

@ -1,375 +0,0 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ooxml.util;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.StringUtil;
import org.apache.poi.util.SuppressForbidden;
import org.apache.xmlbeans.StringEnumAbstractBase;
import org.junit.Test;
import org.junit.internal.TextListener;
import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.reflections.Reflections;
import junit.framework.TestCase;
/**
* Build a 'lite' version of the ooxml-schemas.jar
*
* @author Yegor Kozlov
*/
public final class OOXMLLite {
private static final Pattern SCHEMA_PATTERN = Pattern.compile("schemaorg_apache_xmlbeans/(system|element)/.*\\.xsb");
/**
* Destination directory to copy filtered classes
*/
private File _destDest;
/**
* Directory with the compiled ooxml tests
*/
private File _testDir;
/**
* Reference to the ooxml-schemas.jar
*/
private File _ooxmlJar;
OOXMLLite(String dest, String test, String ooxmlJar) {
_destDest = new File(dest);
_testDir = new File(test);
_ooxmlJar = new File(ooxmlJar);
}
public static void main(String[] args) throws IOException {
System.out.println("Free memory (bytes): " +
Runtime.getRuntime().freeMemory());
long maxMemory = Runtime.getRuntime().maxMemory();
System.out.println("Maximum memory (bytes): " +
(maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));
System.out.println("Total memory (bytes): " +
Runtime.getRuntime().totalMemory());
String dest = null, test = null, ooxml = null;
for (int i = 0; i < args.length; i++) {
switch (args[i]) {
case "-dest":
dest = args[++i]; // lgtm[java/index-out-of-bounds]
break;
case "-test":
test = args[++i]; // lgtm[java/index-out-of-bounds]
break;
case "-ooxml":
ooxml = args[++i]; // lgtm[java/index-out-of-bounds]
break;
}
}
OOXMLLite builder = new OOXMLLite(dest, test, ooxml);
builder.build();
}
void build() throws IOException {
List<Class<?>> lst = new ArrayList<>();
//collect unit tests
String exclude = StringUtil.join("|",
"BaseTestXWorkbook",
"BaseTestXSheet",
"BaseTestXRow",
"BaseTestXCell",
"BaseTestXSSFPivotTable",
"TestSXSSFWorkbook\\$\\d",
"TestUnfixedBugs",
"MemoryUsage",
"TestDataProvider",
"TestDataSamples",
"All.+Tests",
"ZipFileAssert",
"AesZipFileZipEntrySource",
"TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource",
"PkiTestUtils",
"TestCellFormatPart\\$\\d",
"TestSignatureInfo\\$\\d",
"TestCertificateEncryption\\$CertData",
"TestPOIXMLDocument\\$OPCParser",
"TestPOIXMLDocument\\$TestFactory",
"TestXSLFTextParagraph\\$DrawTextParagraphProxy",
"TestXSSFExportToXML\\$\\d",
"TestXSSFExportToXML\\$DummyEntityResolver",
"TestFormulaEvaluatorOnXSSF\\$Result",
"TestFormulaEvaluatorOnXSSF\\$SS",
"TestMultiSheetFormulaEvaluatorOnXSSF\\$Result",
"TestMultiSheetFormulaEvaluatorOnXSSF\\$SS",
"TestXSSFBugs\\$\\d",
"AddImageBench",
"AddImageBench_jmhType_B\\d",
"AddImageBench_benchCreatePicture_jmhTest",
"TestEvilUnclosedBRFixingInputStream\\$EvilUnclosedBRFixingInputStream",
"TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource\\$TempFileRecordingSheetDataWriterWithDecorator",
"TestXSSFBReader\\$1",
"TestXSSFBReader\\$TestSheetHandler",
"TestFormulaEvaluatorOnXSSF\\$1",
"TestMultiSheetFormulaEvaluatorOnXSSF\\$1",
"TestZipPackagePropertiesMarshaller\\$1",
"SLCommonUtils",
"TestPPTX2PNG\\$1",
"TestMatrixFormulasFromXMLSpreadsheet\\$1",
"TestMatrixFormulasFromXMLSpreadsheet\\$Navigator",
"TestPOIXMLDocument\\$UncaughtHandler",
"TestOleShape\\$Api",
"TestOleShape\\$1",
"TestPOIXMLDocument\\$1",
"TestXMLSlideShow\\$1",
"TestXMLSlideShow\\$BufAccessBAOS",
"TestXDDFChart\\$1",
"TestOOXMLLister\\$1",
"TestOOXMLPrettyPrint\\$1"
);
System.out.println("Collecting unit tests from " + _testDir);
collectTests(_testDir, _testDir, lst, ".+.class$", ".+(" + exclude + ").class");
System.out.println("Found " + lst.size() + " classes");
//run tests
JUnitCore jUnitCore = new JUnitCore();
jUnitCore.addListener(new TextListener(System.out) {
private final Set<String> classes = new HashSet<>();
private int count;
@Override
public void testStarted(Description description) {
// count how many test-classes we already saw
classes.add(description.getClassName());
count++;
if(count % 100 == 0) {
System.out.println();
System.out.println(classes.size() + "/" + lst.size() + ": " + description.getDisplayName());
}
super.testStarted(description);
}
});
Result result = jUnitCore.run(lst.toArray(new Class<?>[0]));
if (!result.wasSuccessful()) {
throw new RuntimeException("Tests did not succeed, cannot build ooxml-lite jar");
}
//see what classes from the ooxml-schemas.jar are loaded
System.out.println("Copying classes to " + _destDest);
Set<Class<?>> classes = getLoadedClasses(_ooxmlJar.getName());
Set<String> packages = new HashSet<>();
for (Class<?> cls : classes) {
copyFile(cls);
packages.add(cls.getPackage().getName());
if (cls.isInterface()) {
/// Copy classes and interfaces declared as members of this class
for (Class<?> fc : cls.getDeclaredClasses()) {
copyFile(fc);
}
}
}
for (String pkg : packages) {
Reflections reflections = new Reflections(pkg);
Set<Class<? extends List>> listClasses = reflections.getSubTypesOf(List.class);
listClasses.removeAll(classes);
for (Class listClass : listClasses) {
for (Class<?> compare : classes) {
if (listClass.getName().startsWith(compare.getName())) {
copyFile(listClass);
}
}
}
Set<Class<? extends StringEnumAbstractBase>> enumClasses = reflections.getSubTypesOf(StringEnumAbstractBase.class);
listClasses.removeAll(classes);
for (Class enumClass : enumClasses) {
for (Class<?> compare : classes) {
if (enumClass.getName().startsWith(compare.getName())) {
copyFile(enumClass);
}
}
}
}
//finally copy the compiled .xsb files
System.out.println("Copying .xsb resources");
try (JarFile jar = new JarFile(_ooxmlJar)) {
for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
JarEntry je = e.nextElement();
if (SCHEMA_PATTERN.matcher(je.getName()).matches()) {
File destFile = new File(_destDest, je.getName());
IOUtils.copy(jar.getInputStream(je), destFile);
}
}
}
}
private void copyFile(Class<?> cls) throws IOException {
String className = cls.getName();
String classRef = className.replace('.', '/') + ".class";
File destFile = new File(_destDest, classRef);
IOUtils.copy(cls.getResourceAsStream('/' + classRef), destFile);
}
private static boolean checkForTestAnnotation(Class<?> testclass) {
for (Method m : testclass.getDeclaredMethods()) {
if(m.isAnnotationPresent(Test.class)) {
return true;
}
}
// also check super classes
if(testclass.getSuperclass() != null) {
for (Method m : testclass.getSuperclass().getDeclaredMethods()) {
if(m.isAnnotationPresent(Test.class)) {
return true;
}
}
}
System.out.println("Class " + testclass.getName() + " does not derive from TestCase and does not have a @Test annotation");
// Should we also look at superclasses to find cases
// where we have abstract base classes with derived tests?
// if(checkForTestAnnotation(testclass.getSuperclass())) return true;
return false;
}
/**
* Recursively collect classes from the supplied directory
*
* @param arg the directory to search in
* @param out output
* @param ptrn the pattern (regexp) to filter found files
*/
private static void collectTests(File root, File arg, List<Class<?>> out, String ptrn, String exclude) {
if (arg.isDirectory()) {
File files[] = arg.listFiles();
if (files != null) {
for (File f : files) {
collectTests(root, f, out, ptrn, exclude);
}
}
} else {
String path = arg.getAbsolutePath();
String prefix = root.getAbsolutePath();
String cls = path.substring(prefix.length() + 1).replace(File.separator, ".");
if(!cls.matches(ptrn)) {
return;
}
if (cls.matches(exclude)) {
return;
}
//ignore inner classes defined in tests
if (cls.indexOf('$') != -1) {
System.out.println("Inner class " + cls + " not included");
return;
}
cls = cls.replace(".class", "");
try {
Class<?> testclass = Class.forName(cls);
if (TestCase.class.isAssignableFrom(testclass)
|| checkForTestAnnotation(testclass)) {
out.add(testclass);
}
} catch (Throwable e) { // NOSONAR
System.out.println("Class " + cls + " is not in classpath");
}
}
}
/**
*
* @param ptrn the pattern to filter output
* @return the classes loaded by the system class loader
*/
@SuppressWarnings("unchecked")
private static Set<Class<?>> getLoadedClasses(String ptrn) {
// make the field accessible, we defer this from static initialization to here to
// allow JDKs which do not have this field (e.g. IBM JDK) to at least load the class
// without failing, see https://issues.apache.org/bugzilla/show_bug.cgi?id=56550
final Field _classes = AccessController.doPrivileged(new PrivilegedAction<Field>() {
@Override
@SuppressForbidden("TODO: Reflection works until Java 8 on Oracle/Sun JDKs, but breaks afterwards (different classloader types, access checks)")
public Field run() {
try {
Field fld = ClassLoader.class.getDeclaredField("classes");
fld.setAccessible(true);
return fld;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
ClassLoader appLoader = ClassLoader.getSystemClassLoader();
try {
Vector<Class<?>> classes = (Vector<Class<?>>) _classes.get(appLoader);
Set<Class<?>> set = new HashSet<>();
for (Class<?> cls : classes) {
// e.g. proxy-classes, ...
ProtectionDomain pd = cls.getProtectionDomain();
if (pd == null) {
continue;
}
CodeSource cs = pd.getCodeSource();
if (cs == null) {
continue;
}
URL loc = cs.getLocation();
if (loc == null) {
continue;
}
String jar = loc.toString();
if (jar.contains(ptrn)) {
set.add(cls);
}
}
return set;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,78 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ooxml.util;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
* OOXMLLiteAgent is the replacement for the former OOXMLLite, because in Java 12
* it isn't possible to access the privates :) of the ClassLoader
*/
public class OOXMLLiteAgent {
static class LoggingTransformer implements ClassFileTransformer {
final Path path;
final Pattern includes;
final Set<Integer> fileHashes = new HashSet<>();
public LoggingTransformer(String agentArgs) {
String args[] = (agentArgs == null ? "" : agentArgs).split("\\|",2);
path = Paths.get(args.length >= 1 ? args[0] : "ooxml-lite.out");
includes = Pattern.compile(args.length >= 2 ? args[1] : ".*/schemas/.*");
try {
if (Files.exists(path)) {
try (Stream<String> stream = Files.lines(path)) {
stream.forEach((s) -> fileHashes.add(s.hashCode()));
}
} else {
Files.createFile(path);
}
} catch (IOException ignored) {
}
}
public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) {
if (path != null && className != null && !fileHashes.contains(className.hashCode()) && includes.matcher(className).find()) {
try {
// TODO: check if this is atomic ... as transform() is probably called synchronized, it doesn't matter anyway
Files.write(path, (className+"\n").getBytes(StandardCharsets.ISO_8859_1), StandardOpenOption.APPEND);
fileHashes.add(className.hashCode());
} catch (IOException ignroed) {
}
}
return bytes;
}
}
public static void premain(String agentArgs, Instrumentation inst) {
inst.addTransformer(new LoggingTransformer(agentArgs));
}
}

View File

@ -41,7 +41,7 @@ public class TestCellFormatPart extends CellFormatTestBase {
@BeforeClass
public static void setLocale() {
userLocale = LocaleUtil.getUserLocale();
LocaleUtil.setUserLocale(Locale.ROOT);
LocaleUtil.setUserLocale(Locale.UK);
}
@AfterClass

View File

@ -18,6 +18,7 @@
package org.apache.poi.hmef;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
@ -83,9 +84,7 @@ public final class TestAttachments extends HMEFTest {
List<Attachment> attachments = quick.getAttachments();
// Pick a predictable date format + timezone
DateFormat fmt = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.UK
);
DateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy hh:mm:ss", Locale.UK);
fmt.setTimeZone(LocaleUtil.TIMEZONE_UTC);
// They should all have the same date on them

View File

@ -20,6 +20,7 @@ package org.apache.poi.hmef.attribute;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;
import org.apache.poi.POIDataSamples;
@ -159,9 +160,7 @@ protected void tearDown() throws Exception {
assertEquals(MAPIDateAttribute.class, attr.getClass());
MAPIDateAttribute date = (MAPIDateAttribute)attr;
DateFormat fmt = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.UK
);
DateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss", Locale.UK);
fmt.setTimeZone(LocaleUtil.TIMEZONE_UTC);
assertEquals("15-Dec-2010 14:46:31", fmt.format(date.getDate()));

View File

@ -19,6 +19,7 @@ package org.apache.poi.hmef.attribute;
import java.io.ByteArrayInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;
import org.apache.poi.POIDataSamples;
@ -159,9 +160,7 @@ public final class TestTNEFAttributes extends TestCase {
// Ask for it as a Java date, and have it converted
// Pick a predictable format + location + timezone
TNEFDateAttribute date = (TNEFDateAttribute)attr;
DateFormat fmt = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.UK
);
DateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss", Locale.UK);
fmt.setTimeZone(LocaleUtil.TIMEZONE_UTC);
assertEquals("28-Apr-2010 12:40:56", fmt.format(date.getDate()));
}

View File

@ -23,119 +23,100 @@ import java.text.DateFormatSymbols;
import java.text.FieldPosition;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.poi.util.LocaleUtil;
import org.junit.Test;
public class TestExcelStyleDateFormatter {
private static final String EXCEL_DATE_FORMAT = "MMMMM";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT);
private final int jreVersion;
public TestExcelStyleDateFormatter() {
jreVersion = Integer.parseInt(System.getProperty("java.version")
.replace("1.8", "8").replaceAll("(\\d+).*", "$1"));
}
/**
* [Bug 60369] Month format 'MMMMM' issue with TEXT-formula and Java 8
*/
@Test
public void test60369() throws ParseException {
Map<Locale, List<String>> testMap = initializeLocales();
public void test60369() {
Map<Locale, String> testMap = initializeLocales();
// We have to set up dates as well.
SimpleDateFormat testDateFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.ROOT);
List<Date> testDates = Arrays.asList(
testDateFormat.parse("12.01.1980"),
testDateFormat.parse("11.02.1995"),
testDateFormat.parse("10.03.2045"),
testDateFormat.parse("09.04.2016"),
testDateFormat.parse("08.05.2017"),
testDateFormat.parse("07.06.1945"),
testDateFormat.parse("06.07.1998"),
testDateFormat.parse("05.08.2099"),
testDateFormat.parse("04.09.1988"),
testDateFormat.parse("03.10.2023"),
testDateFormat.parse("02.11.1978"),
testDateFormat.parse("01.12.1890"));
List<Date> testDates = Stream.of("1980-01-12", "1995-02-11", "2045-03-10", "2016-04-09", "2017-05-08",
"1945-06-07", "1998-07-06", "2099-08-05", "1988-09-04", "2023-10-03", "1978-11-02", "1890-12-01")
.map(this::parseDate).collect(Collectors.toList());
// Let's iterate over the test setup.
for (Locale locale : testMap.keySet()) {
ExcelStyleDateFormatter formatter = new ExcelStyleDateFormatter(EXCEL_DATE_FORMAT, new DateFormatSymbols(locale));
for (int i = 0; i < testDates.size(); i++) {
// Call the method to be tested!
String result =
formatter.format(testDates.get(i),
new StringBuffer(),
new FieldPosition(java.text.DateFormat.MONTH_FIELD)).toString();
//System.err.println(result + " - " + getUnicode(result.charAt(0)));
assertEquals("Failed for locale " + locale + ", provider: " + System.getProperty("java.locale.providers") +
" and date " + testDates.get(i) + ", having: " + result,
getUnicode(testMap.get(locale).get(i).charAt(0)), getUnicode(result.charAt(0)));
final String provider = System.getProperty("java.locale.providers");
final FieldPosition fp = new FieldPosition(java.text.DateFormat.MONTH_FIELD);
final ExcelStyleDateFormatter formatter = new ExcelStyleDateFormatter(EXCEL_DATE_FORMAT);
final StringBuffer sb = new StringBuffer();
for (Map.Entry<Locale,String> me : testMap.entrySet()) {
final Locale locale = me.getKey();
final String expected = me.getValue();
formatter.setDateFormatSymbols(DateFormatSymbols.getInstance(locale));
int month = 0;
for (Date d : testDates) {
sb.setLength(0);
String result = formatter.format(d, sb, fp).toString();
String msg = "Failed testDates for locale " + locale + ", provider: " + provider +
" and date " + d + ", having: " + result;
int actIdx = (Locale.CHINESE.equals(locale) && jreVersion >= 12) ? 1 : 0;
assertEquals(msg, expected.charAt(month), result.charAt(actIdx));
month++;
}
}
}
private Map<Locale, List<String>> initializeLocales() {
// Setting up the locale to be tested together with a list of asserted unicode-formatted results and put them in a map.
Locale germanLocale = Locale.GERMAN;
List<String> germanResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d",
"\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044");
Locale russianLocale = new Locale("ru", "RU");
List<String> russianResultList = Arrays.asList("\u044f", "\u0444", "\u043c", "\u0430", "\u043c",
"\u0438", "\u0438", "\u0430", "\u0441", "\u043e", "\u043d", "\u0434");
Locale austrianLocale = new Locale("de", "AT");
List<String> austrianResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d",
"\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044");
Locale englishLocale = Locale.UK;
List<String> englishResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d",
"\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044");
Locale frenchLocale = Locale.FRENCH;
List<String> frenchResultList = Arrays.asList("\u006a", "\u0066", "\u006d", "\u0061", "\u006d",
"\u006a", "\u006a", "\u0061", "\u0073", "\u006f", "\u006e", "\u0064");
Locale chineseLocale = Locale.CHINESE;
List<String> chineseResultList = Arrays.asList("\u4e00", "\u4e8c", "\u4e09", "\u56db", "\u4e94",
"\u516d", "\u4e03", "\u516b", "\u4e5d", "\u5341", "\u5341", "\u5341");
Locale turkishLocale = new Locale("tr", "TR");
List<String> turkishResultList = Arrays.asList("\u004f", "\u015e", "\u004d", "\u004e", "\u004d",
"\u0048", "\u0054", "\u0041", "\u0045", "\u0045", "\u004b", "\u0041");
Locale hungarianLocale = new Locale("hu", "HU");
List<String> hungarianResultList = Arrays.asList("\u006a", "\u0066", "\u006d", "\u00e1", "\u006d",
"\u006a", "\u006a", "\u0061", "\u0073", "\u006f", "\u006e", "\u0064");
Locale indianLocale = new Locale("en", "IN");
List<String> indianResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d",
"\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044");
Locale indonesianLocale = new Locale("in", "ID");
List<String> indonesianResultList = Arrays.asList("\u004a", "\u0046", "\u004d", "\u0041", "\u004d",
"\u004a", "\u004a", "\u0041", "\u0053", "\u004f", "\u004e", "\u0044");
Map<Locale, List<String>> testMap = new HashMap<>();
testMap.put(germanLocale, germanResultList);
testMap.put(russianLocale, russianResultList);
testMap.put(austrianLocale, austrianResultList);
testMap.put(englishLocale, englishResultList);
testMap.put(frenchLocale, frenchResultList);
testMap.put(chineseLocale, chineseResultList);
testMap.put(turkishLocale, turkishResultList);
testMap.put(hungarianLocale, hungarianResultList);
testMap.put(indianLocale, indianResultList);
testMap.put(indonesianLocale, indonesianResultList);
return testMap;
private Date parseDate(String dateStr) {
try {
return DATE_FORMAT.parse(dateStr);
} catch (ParseException e) {
return new Date(0);
}
}
private String getUnicode(char c) {
return "\\u" + Integer.toHexString(c | 0x10000).substring(1);
/**
* Setting up the locale to be tested together with a list of asserted
* unicode-formatted results and put them in a map.
*/
private Map<Locale, String> initializeLocales() {
Map<Locale, String> testMap = new HashMap<>();
testMap.put(Locale.GERMAN, "JFMAMJJASOND");
testMap.put(new Locale("de", "AT"), "JFMAMJJASOND");
testMap.put(Locale.UK, "JFMAMJJASOND");
testMap.put(new Locale("en", "IN"), "JFMAMJJASOND");
testMap.put(new Locale("in", "ID"), "JFMAMJJASOND");
testMap.put(Locale.FRENCH, "jfmamjjasond");
testMap.put(new Locale("ru", "RU"),
"\u044f\u0444\u043c\u0430\u043c\u0438\u0438\u0430\u0441\u043e\u043d\u0434");
testMap.put(Locale.CHINESE, jreVersion < 12
? "\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u5341\u5341"
: "123456789111");
testMap.put(new Locale("tr", "TR"),
"\u004f\u015e\u004d\u004e\u004d\u0048\u0054\u0041\u0045\u0045\u004b\u0041");
testMap.put(new Locale("hu", "HU"),
"\u006a\u0066\u006d\u00e1\u006d\u006a\u006a\u0061\u0073\u006f\u006e\u0064");
return testMap;
}
@Test
@ -150,7 +131,7 @@ public class TestExcelStyleDateFormatter {
try {
LocaleUtil.setUserLocale(Locale.GERMAN);
String dateStr = new ExcelStyleDateFormatter(EXCEL_DATE_FORMAT).format(
new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT).parse("2016-03-26"));
DATE_FORMAT.parse("2016-03-26"));
assertEquals("M", dateStr);
} finally {
LocaleUtil.setUserLocale(before);
@ -160,7 +141,7 @@ public class TestExcelStyleDateFormatter {
@Test
public void testWithPattern() throws ParseException {
String dateStr = new ExcelStyleDateFormatter("yyyy|" + EXCEL_DATE_FORMAT + "|").format(
new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT).parse("2016-03-26"));
DATE_FORMAT.parse("2016-03-26"));
assertEquals("2016|M|", dateStr);
}
}