#744 Improve support for Java 9

When compilig with Java 9 and and source version 1.8 Elements#getTypeElement(CharSequence) returns the types from all modules (such as java.xml.bind or java.xml.datatype).
However if the required modules are not added the classes cannot be used. Therefore, apart from using the Elements we are also checking if the class is also there.

If source version 9 is used then Elements#getTypeElement(CharSequence) works correctly and does not return the types if the modules are not there
This commit is contained in:
Filip Hrisafov 2017-10-17 20:57:05 +02:00 committed by GitHub
parent 98bdc3612f
commit 22e17f9c4b
6 changed files with 173 additions and 19 deletions

View File

@ -25,6 +25,7 @@ import java.text.SimpleDateFormat;
import org.mapstruct.ap.internal.util.JavaTimeConstants;
import org.mapstruct.ap.internal.util.JodaTimeConstants;
import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.XmlConstants;
/**
* Factory for {@link DateFormatValidator}. <p> Based on the types of source / target type a specific {@link
@ -42,7 +43,6 @@ final class DateFormatValidatorFactory {
private static final String ORG_JODA_TIME_FORMAT_DATE_TIME_FORMAT = "org.joda.time.format.DateTimeFormat";
private static final String FOR_PATTERN = "forPattern";
private static final String JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR = "javax.xml.datatype.XMLGregorianCalendar";
private DateFormatValidatorFactory() {
}
@ -85,7 +85,7 @@ final class DateFormatValidatorFactory {
private static boolean isXmlGregorianCalendarSupposedToBeMapped(Type sourceType, Type targetType) {
return typesEqualsOneOf(
sourceType, targetType, JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR );
sourceType, targetType, XmlConstants.JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR );
}
private static boolean isJodaDateTimeSupposed(Type sourceType, Type targetType) {

View File

@ -18,13 +18,14 @@
*/
package org.mapstruct.ap.internal.model.source.builtin;
import java.util.ArrayList;
import java.util.List;
import org.mapstruct.ap.internal.model.common.TypeFactory;
import org.mapstruct.ap.internal.util.Collections;
import org.mapstruct.ap.internal.util.JavaTimeConstants;
import org.mapstruct.ap.internal.util.JaxbConstants;
import org.mapstruct.ap.internal.util.JodaTimeConstants;
import org.mapstruct.ap.internal.util.XmlConstants;
/**
* Registry for all built-in methods.
@ -36,27 +37,32 @@ public class BuiltInMappingMethods {
private final List<BuiltInMethod> builtInMethods;
public BuiltInMappingMethods(TypeFactory typeFactory) {
builtInMethods = Collections.newArrayList(
new DateToXmlGregorianCalendar( typeFactory ),
new XmlGregorianCalendarToDate( typeFactory ),
new StringToXmlGregorianCalendar( typeFactory ),
new XmlGregorianCalendarToString( typeFactory ),
new CalendarToXmlGregorianCalendar( typeFactory ),
new XmlGregorianCalendarToCalendar( typeFactory )
);
boolean isXmlGregorianCalendarPresent = isXmlGregorianCalendarAvailable( typeFactory );
builtInMethods = new ArrayList<BuiltInMethod>( 20 );
if ( isXmlGregorianCalendarPresent ) {
builtInMethods.add( new DateToXmlGregorianCalendar( typeFactory ) );
builtInMethods.add( new XmlGregorianCalendarToDate( typeFactory ) );
builtInMethods.add( new StringToXmlGregorianCalendar( typeFactory ) );
builtInMethods.add( new XmlGregorianCalendarToString( typeFactory ) );
builtInMethods.add( new CalendarToXmlGregorianCalendar( typeFactory ) );
builtInMethods.add( new XmlGregorianCalendarToCalendar( typeFactory ) );
}
if ( isJaxbAvailable( typeFactory ) ) {
builtInMethods.add( new JaxbElemToValue( typeFactory ) );
}
if ( isJava8TimeAvailable( typeFactory ) ) {
builtInMethods.add( new ZonedDateTimeToCalendar( typeFactory ) );
builtInMethods.add( new CalendarToZonedDateTime( typeFactory ) );
builtInMethods.add( new XmlGregorianCalendarToLocalDate( typeFactory ) );
builtInMethods.add( new LocalDateToXmlGregorianCalendar( typeFactory ) );
if ( isXmlGregorianCalendarPresent ) {
builtInMethods.add( new XmlGregorianCalendarToLocalDate( typeFactory ) );
builtInMethods.add( new LocalDateToXmlGregorianCalendar( typeFactory ) );
}
}
if ( isJodaTimeAvailable( typeFactory ) ) {
if ( isJodaTimeAvailable( typeFactory ) && isXmlGregorianCalendarPresent ) {
builtInMethods.add( new JodaDateTimeToXmlGregorianCalendar( typeFactory ) );
builtInMethods.add( new XmlGregorianCalendarToJodaDateTime( typeFactory ) );
builtInMethods.add( new JodaLocalDateTimeToXmlGregorianCalendar( typeFactory ) );
@ -69,13 +75,18 @@ public class BuiltInMappingMethods {
}
private static boolean isJaxbAvailable(TypeFactory typeFactory) {
return typeFactory.isTypeAvailable( JaxbConstants.JAXB_ELEMENT_FQN );
return JaxbConstants.isJaxbElementPresent() && typeFactory.isTypeAvailable( JaxbConstants.JAXB_ELEMENT_FQN );
}
private static boolean isJava8TimeAvailable(TypeFactory typeFactory) {
return typeFactory.isTypeAvailable( JavaTimeConstants.ZONED_DATE_TIME_FQN );
}
private static boolean isXmlGregorianCalendarAvailable(TypeFactory typeFactory) {
return XmlConstants.isXmlGregorianCalendarPresent() &&
typeFactory.isTypeAvailable( XmlConstants.JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR );
}
private static boolean isJodaTimeAvailable(TypeFactory typeFactory) {
return typeFactory.isTypeAvailable( JodaTimeConstants.DATE_TIME_FQN );
}

View File

@ -27,8 +27,6 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlElementRef;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.source.Method;
@ -37,9 +35,11 @@ import org.mapstruct.ap.internal.prism.XmlElementDeclPrism;
import org.mapstruct.ap.internal.prism.XmlElementRefPrism;
/**
* Finds the {@link XmlElementRef} annotation on a field (of the mapping result type or its super types) matching the
* Finds the {@link javax.xml.bind.annotation.XmlElementRef} annotation on a field (of the mapping result type or its
* super types) matching the
* target property name. Then selects those methods with matching {@code name} and {@code scope} attributes of the
* {@link XmlElementDecl} annotation, if that is present. Matching happens in the following order:
* {@link javax.xml.bind.annotation.XmlElementDecl} annotation, if that is present. Matching happens in the following
* order:
* <ol>
* <li>Name and Scope matches</li>
* <li>Scope matches</li>

View File

@ -0,0 +1,89 @@
/**
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed 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.mapstruct.ap.internal.util;
/**
* Utilities for working with classes. It is mainly needed because using the {@link javax.lang.model.util.Elements}
* is not always correct. For example when compiling with JDK 9 and source version 8 classes from different modules
* are available by {@link javax.lang.model.util.Elements#getTypeElement(CharSequence)} but they are actually not
* if those modules are not added during compilation.
*
* @author Filip Hrisafov
*/
class ClassUtils {
private ClassUtils() {
}
/**
* Determine whether the {@link Class} identified by the supplied name is present
* and can be loaded. Will return {@code false} if either the class or
* one of its dependencies is not present or cannot be loaded.
*
* @param className the name of the class to check
* @param classLoader the class loader to use
* (may be {@code null}, which indicates the default class loader)
*
* @return whether the specified class is present
*/
static boolean isPresent(String className, ClassLoader classLoader) {
try {
ClassLoader classLoaderToUse = classLoader;
if ( classLoaderToUse == null ) {
classLoaderToUse = getDefaultClassLoader();
}
classLoaderToUse.loadClass( className );
return true;
}
catch ( ClassNotFoundException ex ) {
// Class or one of its dependencies is not present...
return false;
}
}
/**
* Return the default ClassLoader to use: typically the thread context
* ClassLoader, if available; the ClassLoader that loaded the ClassUtils
* class will be used as fallback.
* <p>Call this method if you intend to use the thread context ClassLoader
* in a scenario where you absolutely need a non-null ClassLoader reference:
* for example, for class path resource loading (but not necessarily for
* {@code Class.forName}, which accepts a {@code null} ClassLoader
* reference as well).
*
* @return the default ClassLoader (never {@code null})
*
* @see Thread#getContextClassLoader()
*/
private static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch ( Throwable ex ) {
// Cannot access thread context ClassLoader - falling back to system class loader...
}
if ( cl == null ) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
}
return cl;
}
}

View File

@ -24,7 +24,18 @@ package org.mapstruct.ap.internal.util;
public final class JaxbConstants {
public static final String JAXB_ELEMENT_FQN = "javax.xml.bind.JAXBElement";
private static final boolean IS_JAXB_ELEMENT_PRESENT = ClassUtils.isPresent(
JAXB_ELEMENT_FQN,
JaxbConstants.class.getClassLoader()
);
private JaxbConstants() {
}
/**
* @return {@code true} if {@link javax.xml.bind.JAXBElement} is present, {@code false} otherwise
*/
public static boolean isJaxbElementPresent() {
return IS_JAXB_ELEMENT_PRESENT;
}
}

View File

@ -0,0 +1,43 @@
/**
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed 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.mapstruct.ap.internal.util;
/**
* Helper holding JAXB time full qualified class names for conversion registration
*
* @author Filip Hrisafov
*/
public final class XmlConstants {
public static final String JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR = "javax.xml.datatype.XMLGregorianCalendar";
private static final boolean IS_XML_GREGORIAN_CALENDAR_PRESENT = ClassUtils.isPresent(
JAVAX_XML_DATATYPE_XMLGREGORIAN_CALENDAR,
XmlConstants.class.getClassLoader()
);
private XmlConstants() {
}
/**
* @return {@code true} if the {@link javax.xml.datatype.XMLGregorianCalendar} is present, {@code false} otherwise
*/
public static boolean isXmlGregorianCalendarPresent() {
return IS_XML_GREGORIAN_CALENDAR_PRESENT;
}
}