diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java
index 649c3c7a7..dddd6d685 100755
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/DateFormatValidatorFactory.java
@@ -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}.
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) {
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java
index 87de5c13c..9675bf386 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/builtin/BuiltInMappingMethods.java
@@ -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 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( 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 );
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java
index 0eae22302..6051ad11e 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/selector/XmlElementDeclSelector.java
@@ -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:
*
* - Name and Scope matches
* - Scope matches
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java b/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java
new file mode 100644
index 000000000..95d5bfbe9
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/ClassUtils.java
@@ -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.
+ * 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;
+ }
+
+}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java
index 7edf655a4..c7c9e6c55 100644
--- a/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/JaxbConstants.java
@@ -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;
+ }
}
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/XmlConstants.java b/processor/src/main/java/org/mapstruct/ap/internal/util/XmlConstants.java
new file mode 100644
index 000000000..5a341d881
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/internal/util/XmlConstants.java
@@ -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;
+ }
+}