package com.sun.tools.xjc.addon.xew; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; import com.sun.codemodel.JAnnotatable; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JAnnotationValue; import com.sun.codemodel.JClass; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JExpr; import com.sun.codemodel.JExpression; import com.sun.codemodel.JFormatter; import com.sun.codemodel.JGenerable; import com.sun.codemodel.JType; import com.sun.codemodel.JVar; import com.sun.tools.xjc.model.CPropertyInfo; import com.sun.tools.xjc.reader.xmlschema.bindinfo.BIDeclaration; import com.sun.tools.xjc.reader.xmlschema.bindinfo.BIProperty; import com.sun.tools.xjc.reader.xmlschema.bindinfo.BindInfo; import com.sun.xml.xsom.XSAnnotation; import com.sun.xml.xsom.XSComponent; import com.sun.xml.xsom.XSDeclaration; import com.sun.xml.xsom.XSParticle; import com.sun.xml.xsom.XSTerm; import org.apache.commons.lang3.reflect.FieldUtils; public final class CommonUtils { /** * Returns {@code true} if given class is hidden, that is not generated & saved by XJC. These are for example * instances of {@link JCodeModel.JReferencedClass} (JVM-wide classes) or instances of {@link JDefinedClass} with * hidden flag (customized super-class or super-interface). */ public static boolean isHiddenClass(JClass clazz) { // See also https://java.net/jira/browse/JAXB-958 return !(clazz instanceof JDefinedClass) || ((JDefinedClass) clazz).isHidden(); } /** * Returns <code>true</code> of the given <code>type</code> is {@link JClass} and contains <code>classToCheck</code> * in the list of parametrisations. */ public static boolean isListedAsParametrisation(JClass classToCheck, JType type) { return type instanceof JClass && ((JClass) type).getTypeParameters().contains(classToCheck); } // // Annotation helpers. // /** * Returns the annotation for the given field. This method has side effect as it initailizes the lazy list of * annotations on the given {@code annotatable}. */ public static JAnnotationUse getAnnotation(JAnnotatable annotatable, JClass annotationClass) { for (JAnnotationUse annotation : annotatable.annotations()) { if (annotation.getAnnotationClass().equals(annotationClass)) { return annotation; } } return null; } /** * Returns the annotation element as {@link JAnnotationValue}. */ public static JAnnotationValue getAnnotationMember(JAnnotationUse annotation, String annotationMember) { if (annotation == null) { return null; } // FIXME: Workaround for https://java.net/jira/browse/JAXB-1040: Map<String, JAnnotationValue> memberValues = getPrivateField(annotation, "memberValues"); if (memberValues == null) { return null; } return memberValues.get(annotationMember); } /** * Returns the value of annotation element as {@link JExpression}. For example, for annotation * <code>@XmlElementRef(name = "last-name", namespace = "http://mycompany.org/exchange", type = JAXBElement.class)</code> * for member <code>name</code> the value <code>last-name</code> will be returned. */ public static JExpression getAnnotationMemberExpression(JAnnotationUse annotation, String annotationMember) { JAnnotationValue annotationValue = getAnnotationMember(annotation, annotationMember); if (annotationValue == null) { return null; } // FIXME: Pending for https://java.net/jira/browse/JAXB-878 try { // In most cases the value is some expression... return (JExpression) getPrivateField(annotationValue, "value"); } catch (IllegalArgumentException e) { // ... and in some cases (like enum) do the conversion from JGenerable to JExpression // (a bit unoptimal, since this expression is going to be converted back to string) return JExpr.lit(generableToString(annotationValue)); } } /** * Append the given {@code annotation} to list of annotations for the given {@code field}. */ public static void addAnnotation(JVar field, JAnnotationUse annotation) { List<JAnnotationUse> annotations = getPrivateField(field, "annotations"); annotations.add(annotation); } /** * Remove the given {@code annotation} from the list of annotations for the given {@code field}. */ public static void removeAnnotation(JVar field, JAnnotationUse annotation) { List<JAnnotationUse> annotations = getPrivateField(field, "annotations"); annotations.remove(annotation); } /** * Check that given field property has name customization ({@code <jaxb:property name="..." />}). * * @see com.sun.xml.bind.api.impl.NameUtil * @see com.sun.codemodel.JJavaName * @see com.sun.tools.xjc.reader.xmlschema.bindinfo.BIProperty#getCustomization(XSComponent) */ public static boolean hasPropertyNameCustomization(CPropertyInfo fieldPropertyInfo) { XSAnnotation annotation = fieldPropertyInfo.getSchemaComponent().getAnnotation(); if (annotation == null) { annotation = getXsdDeclaration(fieldPropertyInfo).getAnnotation(); } if (annotation == null || !(annotation.getAnnotation() instanceof BindInfo)) { return false; } for (BIDeclaration declaration : (BindInfo) annotation.getAnnotation()) { if (declaration instanceof BIProperty) { return true; } } return false; } /** * Returns the string value of passed argument. */ public static final String generableToString(JGenerable generable) { // There is hardly any clean universal way to get the value from e.g. JExpression except of serializing it. // Compare JStringLiteral and JExp#dotclass(). Writer w = new StringWriter(); generable.generate(new JFormatter(w)); // FIXME: Hopefully nobody will put quotes into annotation member value. return w.toString().replace("\"", ""); } // // Reflection helpers. // /** * Set the {@code newValue} to private field {@code fieldName} of given object {@code obj}. * * @throws NoSuchFieldException * if given field was not found */ public static void setPrivateField(Object obj, String fieldName, Object newValue) { try { FieldUtils.writeField(obj, fieldName, newValue, true); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } /** * Get the value of private field {@code fieldName} of given object {@code obj}. * * @throws IllegalArgumentException * if given field was not found */ @SuppressWarnings("unchecked") public static <T> T getPrivateField(Object obj, String fieldName) { try { return (T) FieldUtils.readField(obj, fieldName, true); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } /** * Perform the copying of all fields from {@code src} to {@code dest}. The code was copied from * {@link org.springframework.util.ReflectionUtils#shallowCopyFieldState(Object, Object)}. */ public static <S, D extends S> void copyFields(final S src, D dest) throws IllegalArgumentException { Class<?> targetClass = src.getClass(); do { Field[] fields = targetClass.getDeclaredFields(); for (Field field : fields) { // Skip static fields: if (Modifier.isStatic(field.getModifiers())) { continue; } try { if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { field.setAccessible(true); } Object srcValue = field.get(src); field.set(dest, srcValue); } catch (IllegalAccessException ex) { throw new IllegalStateException( "Shouldn't be illegal to access field '" + field.getName() + "': " + ex); } } targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); } /** * Returns XSD declaration of given property. */ public static XSDeclaration getXsdDeclaration(CPropertyInfo propertyInfo) { XSComponent schemaComponent = propertyInfo.getSchemaComponent(); if (!(schemaComponent instanceof XSParticle)) { // XSComplexType for example: return null; } XSTerm term = ((XSParticle) schemaComponent).getTerm(); if (!(term instanceof XSDeclaration)) { // XSModelGroup for example: return null; } return (XSDeclaration) term; } }