=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeAnnotation.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeAnnotation.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeAnnotation.java 2014-06-09 14:26:55 +0000 @@ -0,0 +1,43 @@ +package org.hisp.dhis.node.annotation; + +/* + * Copyright (c) 2004-2014, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + */ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Morten Olav Hansen + */ +@Target( { ElementType.TYPE } ) +@Retention( RetentionPolicy.RUNTIME ) +public @interface NodeAnnotation +{ +} === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeCollection.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeCollection.java 2014-06-09 12:27:37 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeCollection.java 2014-06-09 14:26:55 +0000 @@ -38,6 +38,7 @@ */ @Target( { ElementType.FIELD } ) @Retention( RetentionPolicy.RUNTIME ) +@NodeAnnotation public @interface NodeCollection { String value() default ""; === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeComplex.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeComplex.java 2014-06-09 12:27:37 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeComplex.java 2014-06-09 14:26:55 +0000 @@ -38,6 +38,7 @@ */ @Target( { ElementType.FIELD } ) @Retention( RetentionPolicy.RUNTIME ) +@NodeAnnotation public @interface NodeComplex { String value() default ""; === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeRoot.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeRoot.java 2014-06-09 12:27:37 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeRoot.java 2014-06-09 14:26:55 +0000 @@ -38,6 +38,7 @@ */ @Target( { ElementType.TYPE } ) @Retention( RetentionPolicy.RUNTIME ) +@NodeAnnotation public @interface NodeRoot { String value() default ""; === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeSimple.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeSimple.java 2014-06-09 12:27:37 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/node/annotation/NodeSimple.java 2014-06-09 14:26:55 +0000 @@ -38,6 +38,7 @@ */ @Target( { ElementType.FIELD } ) @Retention( RetentionPolicy.RUNTIME ) +@NodeAnnotation public @interface NodeSimple { String value() default ""; @@ -46,7 +47,5 @@ boolean isPersisted() default true; - String persistedName() default ""; - String namespace() default ""; } === added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/AbstractPropertyIntrospectorService.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/AbstractPropertyIntrospectorService.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/AbstractPropertyIntrospectorService.java 2014-06-09 14:26:55 +0000 @@ -0,0 +1,68 @@ +package org.hisp.dhis.schema; + +/* + * Copyright (c) 2004-2014, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + */ + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import java.util.List; +import java.util.Map; + +/** + * @author Morten Olav Hansen + */ +public abstract class AbstractPropertyIntrospectorService implements PropertyIntrospectorService +{ + private Map, Map> classMapCache = Maps.newHashMap(); + + @Override + public List getProperties( Class klass ) + { + return Lists.newArrayList( getPropertiesMap( klass ).values() ); + } + + @Override + public Map getPropertiesMap( Class klass ) + { + if ( !classMapCache.containsKey( klass ) ) + { + classMapCache.put( klass, scanClass( klass ) ); + } + + return classMapCache.get( klass ); + } + + /** + * Introspect a class and return a map with key=property-name, and value=Property class. + * + * @param klass Class to scan + * @return Map with key=property-name, and value=Property class + */ + protected abstract Map scanClass( Class klass ); +} === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/Property.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/Property.java 2014-06-09 12:27:37 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/schema/Property.java 2014-06-09 14:26:55 +0000 @@ -35,6 +35,8 @@ import org.hisp.dhis.common.DxfNamespaces; import org.hisp.dhis.common.IdentifiableObject; import org.hisp.dhis.common.NameableObject; +import org.hisp.dhis.node.annotation.NodeRoot; +import org.hisp.dhis.node.annotation.NodeSimple; import java.lang.reflect.Method; @@ -42,16 +44,19 @@ * @author Morten Olav Hansen */ @JacksonXmlRootElement( localName = "property", namespace = DxfNamespaces.DXF_2_0 ) +@NodeRoot public class Property { /** * Class for property. */ + @NodeSimple private Class klass; /** * If this property is a collection, this is the class of the items inside the collection. */ + @NodeSimple private Class itemKlass; /** @@ -68,39 +73,52 @@ * Name for this property, if this class is a collection, it is the name of the items -inside- the collection * and not the collection wrapper itself. */ + @NodeSimple private String name; /** * Name for actual field, used to persistence operations and getting setter/getter. */ + @NodeSimple private String fieldName; /** * Is this property persisted somewhere. This property will be used to create criteria queries * on demand (default: true) */ + @NodeSimple private boolean persisted = true; /** * Name of collection wrapper. */ + @NodeSimple private String collectionName; /** + * If this Property is a collection, should it be wrapped with collectionName? + */ + @NodeSimple + private boolean collectionWrapping; + + /** * Description if provided, will be fetched from @Description annotation. * * @see org.hisp.dhis.common.annotation.Description */ + @NodeSimple private String description; /** * XML-Namespace used for this property. */ + @NodeSimple private String namespace; /** * Usually only used for XML. Is this property considered an attribute. */ + @NodeSimple private boolean attribute; /** @@ -109,6 +127,7 @@ * of the collection, e.g. List would set simple to be true, but List would set it * to false. */ + @NodeSimple private boolean simple; /** @@ -116,6 +135,7 @@ * * @see java.util.Collection */ + @NodeSimple private boolean collection; /** @@ -123,6 +143,7 @@ * * @see org.hisp.dhis.common.IdentifiableObject */ + @NodeSimple private boolean identifiableObject; /** @@ -130,21 +151,23 @@ * * @see org.hisp.dhis.common.NameableObject */ + @NodeSimple private boolean nameableObject; public Property() { } - public Property( Method getterMethod ) + public Property( Class klass ) { - this.getterMethod = getterMethod; + this.klass = klass; } - public Property( Method getterMethod, Class klass ) + public Property( Class klass, Method getter, Method setter ) { - this.getterMethod = getterMethod; - setKlass( klass ); + this( klass ); + this.getterMethod = getter; + this.setterMethod = setter; } @JsonProperty @@ -238,6 +261,18 @@ @JsonProperty @JacksonXmlProperty( namespace = DxfNamespaces.DXF_2_0 ) + public boolean isCollectionWrapping() + { + return collectionWrapping; + } + + public void setCollectionWrapping( boolean collectionWrapping ) + { + this.collectionWrapping = collectionWrapping; + } + + @JsonProperty + @JacksonXmlProperty( namespace = DxfNamespaces.DXF_2_0 ) public String getDescription() { return description; === added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/Jackson2PropertyIntrospectorService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/Jackson2PropertyIntrospectorService.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/Jackson2PropertyIntrospectorService.java 2014-06-09 14:26:55 +0000 @@ -0,0 +1,210 @@ +package org.hisp.dhis.schema; + +/* + * Copyright (c) 2004-2014, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.hisp.dhis.common.IdentifiableObject; +import org.hisp.dhis.common.NameableObject; +import org.hisp.dhis.common.annotation.Description; +import org.hisp.dhis.system.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Default PropertyIntrospectorService implementation that uses Reflection and Jackson annotations + * for reading in properties. + * + * @author Morten Olav Hansen + */ +public class Jackson2PropertyIntrospectorService extends AbstractPropertyIntrospectorService +{ + protected Map scanClass( Class clazz ) + { + Map propertyMap = Maps.newHashMap(); + + // TODO this is quite nasty, should find a better way of exposing properties at class-level + if ( clazz.isAnnotationPresent( JacksonXmlRootElement.class ) ) + { + Property property = new Property(); + + JacksonXmlRootElement jacksonXmlRootElement = clazz.getAnnotation( JacksonXmlRootElement.class ); + + if ( !StringUtils.isEmpty( jacksonXmlRootElement.localName() ) ) + { + property.setName( jacksonXmlRootElement.localName() ); + } + + if ( !StringUtils.isEmpty( jacksonXmlRootElement.namespace() ) ) + { + property.setNamespace( jacksonXmlRootElement.namespace() ); + } + + propertyMap.put( "__self__", property ); + } + + List properties = collectProperties( clazz ); + + for ( Property property : properties ) + { + Method method = property.getGetterMethod(); + JsonProperty jsonProperty = method.getAnnotation( JsonProperty.class ); + + String name = jsonProperty.value(); + + if ( StringUtils.isEmpty( name ) ) + { + String[] getters = new String[]{ + "is", "has", "get" + }; + + name = method.getName(); + + for ( String getter : getters ) + { + if ( name.startsWith( getter ) ) + { + name = name.substring( getter.length() ); + } + } + + name = StringUtils.uncapitalize( name ); + } + + if ( method.isAnnotationPresent( Description.class ) ) + { + Description description = method.getAnnotation( Description.class ); + property.setDescription( description.value() ); + } + + property.setName( name ); + + if ( method.isAnnotationPresent( JacksonXmlProperty.class ) ) + { + JacksonXmlProperty jacksonXmlProperty = method.getAnnotation( JacksonXmlProperty.class ); + + if ( StringUtils.isEmpty( jacksonXmlProperty.localName() ) ) + { + property.setName( name ); + } + else + { + property.setName( jacksonXmlProperty.localName() ); + } + + if ( !StringUtils.isEmpty( jacksonXmlProperty.namespace() ) ) + { + property.setNamespace( jacksonXmlProperty.namespace() ); + } + + property.setAttribute( jacksonXmlProperty.isAttribute() ); + } + + if ( method.isAnnotationPresent( JacksonXmlElementWrapper.class ) ) + { + JacksonXmlElementWrapper jacksonXmlElementWrapper = method.getAnnotation( JacksonXmlElementWrapper.class ); + + // TODO what if element-wrapper have different namespace? + if ( !StringUtils.isEmpty( jacksonXmlElementWrapper.localName() ) ) + { + property.setCollectionName( jacksonXmlElementWrapper.localName() ); + } + } + + propertyMap.put( name, property ); + + Class returnType = method.getReturnType(); + property.setKlass( returnType ); + + if ( Collection.class.isAssignableFrom( returnType ) ) + { + property.setCollection( true ); + + Type type = method.getGenericReturnType(); + + if ( ParameterizedType.class.isInstance( type ) ) + { + ParameterizedType parameterizedType = (ParameterizedType) type; + Class klass = (Class) parameterizedType.getActualTypeArguments()[0]; + property.setItemKlass( klass ); + + if ( collectProperties( klass ).isEmpty() ) + { + property.setSimple( true ); + } + + if ( IdentifiableObject.class.isAssignableFrom( klass ) ) + { + property.setIdentifiableObject( true ); + + if ( NameableObject.class.isAssignableFrom( klass ) ) + { + property.setNameableObject( true ); + } + } + } + } + else + { + if ( collectProperties( returnType ).isEmpty() ) + { + property.setSimple( true ); + } + } + } + + return propertyMap; + } + + private static List collectProperties( Class klass ) + { + List allMethods = ReflectionUtils.getAllMethods( klass ); + List properties = Lists.newArrayList(); + + for ( Method method : allMethods ) + { + if ( method.isAnnotationPresent( JsonProperty.class ) ) + { + properties.add( new Property( klass, method, null ) ); + } + } + + return properties; + } +} === removed file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/JacksonPropertyIntrospectorService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/JacksonPropertyIntrospectorService.java 2014-06-09 12:36:19 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/JacksonPropertyIntrospectorService.java 1970-01-01 00:00:00 +0000 @@ -1,235 +0,0 @@ -package org.hisp.dhis.schema; - -/* - * Copyright (c) 2004-2014, University of Oslo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of the HISP project nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.hisp.dhis.common.IdentifiableObject; -import org.hisp.dhis.common.NameableObject; -import org.hisp.dhis.common.annotation.Description; -import org.hisp.dhis.system.util.ReflectionUtils; -import org.springframework.util.StringUtils; - -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * Default PropertyIntrospectorService implementation that uses Reflection and Jackson annotations - * for reading in properties. - * - * @author Morten Olav Hansen - */ -public class JacksonPropertyIntrospectorService implements PropertyIntrospectorService -{ - @Override - public List getProperties( Class klass ) - { - return Lists.newArrayList( scanClass( klass ).values() ); - } - - @Override - public Map getPropertiesMap( Class klass ) - { - return scanClass( klass ); - } - - // ------------------------------------------------------------------------- - // Scanning Helpers - // ------------------------------------------------------------------------- - - private static Map, Map> classMapCache = Maps.newHashMap(); - - private static Map scanClass( Class clazz ) - { - if ( classMapCache.containsKey( clazz ) ) - { - return classMapCache.get( clazz ); - } - - Map propertyMap = Maps.newHashMap(); - - // TODO this is quite nasty, should find a better way of exposing properties at class-level - if ( clazz.isAnnotationPresent( JacksonXmlRootElement.class ) ) - { - Property property = new Property(); - - JacksonXmlRootElement jacksonXmlRootElement = clazz.getAnnotation( JacksonXmlRootElement.class ); - - if ( !StringUtils.isEmpty( jacksonXmlRootElement.localName() ) ) - { - property.setName( jacksonXmlRootElement.localName() ); - } - - if ( !StringUtils.isEmpty( jacksonXmlRootElement.namespace() ) ) - { - property.setNamespace( jacksonXmlRootElement.namespace() ); - } - - propertyMap.put( "__self__", property ); - } - - List properties = collectProperties( clazz ); - - for ( Property property : properties ) - { - Method method = property.getGetterMethod(); - JsonProperty jsonProperty = method.getAnnotation( JsonProperty.class ); - - String name = jsonProperty.value(); - - if ( StringUtils.isEmpty( name ) ) - { - String[] getters = new String[]{ - "is", "has", "get" - }; - - name = method.getName(); - - for ( String getter : getters ) - { - if ( name.startsWith( getter ) ) - { - name = name.substring( getter.length() ); - } - } - - name = StringUtils.uncapitalize( name ); - } - - if ( method.isAnnotationPresent( Description.class ) ) - { - Description description = method.getAnnotation( Description.class ); - property.setDescription( description.value() ); - } - - property.setName( name ); - - if ( method.isAnnotationPresent( JacksonXmlProperty.class ) ) - { - JacksonXmlProperty jacksonXmlProperty = method.getAnnotation( JacksonXmlProperty.class ); - - if ( StringUtils.isEmpty( jacksonXmlProperty.localName() ) ) - { - property.setName( name ); - } - else - { - property.setName( jacksonXmlProperty.localName() ); - } - - if ( !StringUtils.isEmpty( jacksonXmlProperty.namespace() ) ) - { - property.setNamespace( jacksonXmlProperty.namespace() ); - } - - property.setAttribute( jacksonXmlProperty.isAttribute() ); - } - - if ( method.isAnnotationPresent( JacksonXmlElementWrapper.class ) ) - { - JacksonXmlElementWrapper jacksonXmlElementWrapper = method.getAnnotation( JacksonXmlElementWrapper.class ); - - // TODO what if element-wrapper have different namespace? - if ( !StringUtils.isEmpty( jacksonXmlElementWrapper.localName() ) ) - { - property.setCollectionName( jacksonXmlElementWrapper.localName() ); - } - } - - propertyMap.put( name, property ); - - Class returnType = method.getReturnType(); - property.setKlass( returnType ); - - if ( Collection.class.isAssignableFrom( returnType ) ) - { - property.setCollection( true ); - - Type type = method.getGenericReturnType(); - - if ( ParameterizedType.class.isInstance( type ) ) - { - ParameterizedType parameterizedType = (ParameterizedType) type; - Class klass = (Class) parameterizedType.getActualTypeArguments()[0]; - property.setItemKlass( klass ); - - if ( collectProperties( klass ).isEmpty() ) - { - property.setSimple( true ); - } - - if ( IdentifiableObject.class.isAssignableFrom( klass ) ) - { - property.setIdentifiableObject( true ); - - if ( NameableObject.class.isAssignableFrom( klass ) ) - { - property.setNameableObject( true ); - } - } - } - } - else - { - if ( collectProperties( returnType ).isEmpty() ) - { - property.setSimple( true ); - } - } - } - - classMapCache.put( clazz, propertyMap ); - - return propertyMap; - } - - private static List collectProperties( Class clazz ) - { - List allMethods = ReflectionUtils.getAllMethods( clazz ); - List properties = Lists.newArrayList(); - - for ( Method method : allMethods ) - { - if ( method.isAnnotationPresent( JsonProperty.class ) ) - { - properties.add( new Property( method ) ); - } - } - - return properties; - } -} === added file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/NodePropertyIntrospectorService.java' --- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/NodePropertyIntrospectorService.java 1970-01-01 00:00:00 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/schema/NodePropertyIntrospectorService.java 2014-06-09 14:26:55 +0000 @@ -0,0 +1,190 @@ +package org.hisp.dhis.schema; + +/* + * Copyright (c) 2004-2014, University of Oslo + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of the HISP project nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + */ + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.primitives.Primitives; +import org.hisp.dhis.common.IdentifiableObject; +import org.hisp.dhis.common.NameableObject; +import org.hisp.dhis.node.annotation.NodeAnnotation; +import org.hisp.dhis.node.annotation.NodeCollection; +import org.hisp.dhis.node.annotation.NodeComplex; +import org.hisp.dhis.node.annotation.NodeSimple; +import org.hisp.dhis.system.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Morten Olav Hansen + */ +public class NodePropertyIntrospectorService extends AbstractPropertyIntrospectorService +{ + @Override + protected Map scanClass( Class klass ) + { + Map propertyMap = Maps.newHashMap(); + + for ( Field field : ReflectionUtils.getAllFields( klass ) ) + { + Property property = null; + + for ( Annotation annotation : field.getAnnotations() ) + { + // search for and add all annotations that meta-annotated with NodeAnnotation + if ( annotation.annotationType().isAnnotationPresent( NodeAnnotation.class ) ) + { + Method getter = getGetter( klass, field ); + Method setter = getSetter( klass, field ); + + property = new Property( Primitives.wrap( field.getType() ), getter, setter ); + property.setName( field.getName() ); + property.setFieldName( field.getName() ); + + if ( Collection.class.isAssignableFrom( field.getType() ) ) + { + property.setCollection( true ); + property.setCollectionName( field.getName() ); + + Type type = field.getGenericType(); + + if ( ParameterizedType.class.isInstance( type ) ) + { + ParameterizedType parameterizedType = (ParameterizedType) type; + Class itemKlass = (Class) parameterizedType.getActualTypeArguments()[0]; + property.setItemKlass( itemKlass ); + + if ( IdentifiableObject.class.isAssignableFrom( itemKlass ) ) + { + property.setIdentifiableObject( true ); + + if ( NameableObject.class.isAssignableFrom( itemKlass ) ) + { + property.setNameableObject( true ); + } + } + } + } + + break; + } + } + + if ( property == null ) + { + continue; + } + + if ( field.isAnnotationPresent( NodeSimple.class ) ) + { + NodeSimple nodeSimple = field.getAnnotation( NodeSimple.class ); + property.setSimple( true ); + property.setAttribute( nodeSimple.isAttribute() ); + property.setPersisted( nodeSimple.isPersisted() ); + property.setNamespace( nodeSimple.namespace() ); + + if ( !StringUtils.isEmpty( nodeSimple.value() ) ) + { + property.setName( nodeSimple.value() ); + } + } + else if ( field.isAnnotationPresent( NodeComplex.class ) ) + { + NodeComplex nodeComplex = field.getAnnotation( NodeComplex.class ); + property.setSimple( false ); + property.setNamespace( nodeComplex.namespace() ); + + if ( !StringUtils.isEmpty( nodeComplex.value() ) ) + { + property.setName( nodeComplex.value() ); + } + } + else if ( field.isAnnotationPresent( NodeCollection.class ) ) + { + NodeCollection nodeCollection = field.getAnnotation( NodeCollection.class ); + property.setCollectionWrapping( nodeCollection.useWrapping() ); + + if ( !StringUtils.isEmpty( nodeCollection.value() ) ) + { + property.setCollectionName( nodeCollection.value() ); + } + + if ( !StringUtils.isEmpty( nodeCollection.itemName() ) ) + { + property.setName( nodeCollection.itemName() ); + } + } + + propertyMap.put( property.getName(), property ); + } + + return propertyMap; + } + + private Method getGetter( Class klass, Field field ) + { + return getMethodWithPrefix( klass, field, Lists.newArrayList( "get", "is", "has" ), false ); + } + + private Method getSetter( Class klass, Field field ) + { + return getMethodWithPrefix( klass, field, Lists.newArrayList( "set" ), true ); + } + + private Method getMethodWithPrefix( Class klass, Field field, List prefixes, boolean includeType ) + { + String name = StringUtils.capitalize( field.getName() ); + + for ( String prefix : prefixes ) + { + try + { + Method method = includeType ? klass.getMethod( prefix + name, field.getType() ) : klass.getMethod( prefix + name ); + + if ( method != null ) + { + return method; + } + } + catch ( NoSuchMethodException ignored ) + { + } + } + + return null; + } +} === modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml' --- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2014-06-09 12:36:19 +0000 +++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2014-06-09 14:26:55 +0000 @@ -15,7 +15,7 @@ - + === modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SchemaController.java' --- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SchemaController.java 2014-06-09 12:34:28 +0000 +++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/SchemaController.java 2014-06-09 14:26:55 +0000 @@ -43,7 +43,7 @@ * @author Morten Olav Hansen */ @Controller -@RequestMapping(value = "/schemas", method = RequestMethod.GET) +@RequestMapping( value = "/schemas", method = RequestMethod.GET ) public class SchemaController { @Autowired @@ -61,7 +61,7 @@ return schemaService.getSchemaBySingularName( type ); } - @RequestMapping(value = "/{type}/{property}") + @RequestMapping( value = "/{type}/{property}" ) public @ResponseBody Property getSchemaProperty( @PathVariable String type, @PathVariable String property ) { Schema schema = schemaService.getSchemaBySingularName( type );