/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can * select the license that you prefer but you may not use this file except in * compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet S.A.S. */ package org.restlet.ext.apispark.internal.introspection.application; import java.beans.BeanInfo; import java.beans.PropertyDescriptor; import java.util.Arrays; import java.util.List; import java.util.logging.Logger; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.fasterxml.jackson.annotation.JsonRootName; import com.fasterxml.jackson.databind.introspect.AnnotatedClass; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; import org.restlet.engine.Engine; import org.restlet.engine.util.BeanInfoUtils; import org.restlet.engine.util.StringUtils; import org.restlet.ext.apispark.Introspector; import org.restlet.ext.apispark.internal.introspection.IntrospectionHelper; import org.restlet.ext.apispark.internal.introspection.util.TypeInfo; import org.restlet.ext.apispark.internal.introspection.util.Types; import org.restlet.ext.apispark.internal.introspection.util.UnsupportedTypeException; import org.restlet.ext.apispark.internal.model.Property; import org.restlet.ext.apispark.internal.model.Representation; import org.restlet.ext.apispark.internal.model.Section; /** * @author Manuel Boillod */ public class RepresentationCollector { private static Logger LOGGER = Engine.getLogger(Introspector.class); /** * Returns the description of the given class as a {@link Representation}. * * @param typeInfo * The typeInfo to document. * @param introspectionHelper * The introspector helpers. */ public static void addRepresentation(CollectInfo collectInfo, TypeInfo typeInfo, List<? extends IntrospectionHelper> introspectionHelper) { // Introspect the java class Representation representation = new Representation(); representation.setDescription(""); if (typeInfo.isList()) { // Collect generic type addRepresentation(collectInfo, typeInfo.getComponentTypeInfo(), introspectionHelper); return; } if (typeInfo.isPrimitive() || typeInfo.isFile()) { // primitives and files are not collected return; } // Example: "java.util.Contact" or "String" representation.setDescription("Java type: " + typeInfo.getRepresentationClazz().getName()); // Sections if (collectInfo.isUseSectionNamingPackageStrategy()) { String packageName = typeInfo.getClazz().getPackage().getName(); representation.getSections().add(packageName); if (collectInfo.getSection(packageName) == null) { collectInfo.addSection(new Section(packageName)); } } // Example: "Contact" JsonRootName jsonType = typeInfo.getClazz().getAnnotation(JsonRootName.class); String typeName = jsonType == null ? typeInfo.getRepresentationClazz() .getSimpleName() : jsonType.value(); representation.setName(typeName); representation.setRaw(false); // at this point, identifier is known - we check if it exists in cache boolean notInCache = collectInfo.getRepresentation(representation .getName()) == null; if (notInCache) { // add representation in cache before complete it to avoid infinite // loop collectInfo.addRepresentation(representation); if (typeInfo.isPojo()) { // add properties definition BeanInfo beanInfo = BeanInfoUtils.getBeanInfo(typeInfo .getRepresentationClazz()); JsonIgnoreProperties jsonIgnorePropertiesAnnotation = AnnotatedClass.construct(typeInfo.getRepresentationClazz(), new JacksonAnnotationIntrospector(), null).getAnnotation(JsonIgnoreProperties.class); List<String> jsonIgnoreProperties = jsonIgnorePropertiesAnnotation == null ? null : Arrays.asList(jsonIgnorePropertiesAnnotation.value()); for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) { if (jsonIgnoreProperties != null && jsonIgnoreProperties.contains(pd.getName())) { //ignore this field continue; } JsonIgnore jsonIgnore = pd.getReadMethod().getAnnotation(JsonIgnore.class); if (jsonIgnore != null && jsonIgnore.value()) { //ignore this field continue; } TypeInfo propertyTypeInfo; try { propertyTypeInfo = Types.getTypeInfo(pd.getReadMethod() .getReturnType(), pd.getReadMethod() .getGenericReturnType()); } catch (UnsupportedTypeException e) { LOGGER.warning("Could not add property " + pd.getName() + " of representation " + typeInfo.getRepresentationClazz().getName() + ". " + e.getMessage()); continue; } JsonProperty jsonProperty = pd.getReadMethod().getAnnotation(JsonProperty.class); String propertyName = jsonProperty != null && !StringUtils.isNullOrEmpty(jsonProperty.value()) ? jsonProperty.value() : pd.getName(); JsonPropertyDescription jsonPropertyDescription = pd.getReadMethod().getAnnotation(JsonPropertyDescription.class); // Types Property property = new Property(); property.setName(propertyName); property.setDescription(jsonPropertyDescription != null ? jsonPropertyDescription.value() : ""); property.setType(propertyTypeInfo.getRepresentationName()); property.setMinOccurs(jsonProperty != null && jsonProperty.required() ? 1 : 0); property.setMaxOccurs(propertyTypeInfo.isList() ? -1 : 1); addRepresentation(collectInfo, propertyTypeInfo, introspectionHelper); for (IntrospectionHelper helper : introspectionHelper) { helper.processProperty(property, pd.getReadMethod()); } representation.getProperties().add(property); } } for (IntrospectionHelper helper : introspectionHelper) { helper.processRepresentation(representation, typeInfo.getRepresentationClazz()); } } } }