001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.isis.core.progmodel.layout.ordermethod;
018    
019    import java.lang.reflect.InvocationTargetException;
020    import java.lang.reflect.Method;
021    import java.util.Arrays;
022    import java.util.List;
023    
024    import org.apache.log4j.Logger;
025    
026    import org.apache.isis.core.commons.lang.JavaClassUtils;
027    import org.apache.isis.core.commons.lang.StringUtils;
028    import org.apache.isis.core.metamodel.facets.FacetedMethod;
029    import org.apache.isis.core.metamodel.facets.object.orderactions.ActionOrderFacet;
030    import org.apache.isis.core.metamodel.facets.object.orderfields.FieldOrderFacet;
031    import org.apache.isis.core.metamodel.layout.MemberLayoutArranger;
032    import org.apache.isis.core.metamodel.layout.OrderSet;
033    import org.apache.isis.core.metamodel.layout.ordermethod.SimpleOrderSet;
034    import org.apache.isis.core.metamodel.methodutils.MethodFinderUtils;
035    import org.apache.isis.core.metamodel.methodutils.MethodScope;
036    import org.apache.isis.core.metamodel.spec.ObjectSpecification;
037    
038    public class MemberLayoutArrangerUsingOrderMethod implements MemberLayoutArranger {
039        
040    
041        private static final Logger LOG = Logger.getLogger(MemberLayoutArrangerUsingOrderMethod.class);
042    
043        private static final Object[] NO_PARAMETERS = new Object[0];
044        private static final Class<?>[] NO_PARAMETERS_TYPES = new Class[0];
045    
046        private static final String FIELD_PREFIX = null;
047    
048        private static final String ACTION_PREFIX = null;
049    
050    
051        // ////////////////////////////////////////////////////////////////////////////
052        // constructor
053        // ////////////////////////////////////////////////////////////////////////////
054    
055        public MemberLayoutArrangerUsingOrderMethod() { }
056    
057    
058        // ////////////////////////////////////////////////////////////////////////////
059        // associations
060        // ////////////////////////////////////////////////////////////////////////////
061    
062    
063        @Override
064        public OrderSet createAssociationOrderSetFor(ObjectSpecification spec, final List<FacetedMethod> associationMethods) {
065            if (LOG.isDebugEnabled()) {
066                LOG.debug("MemberLayoutArrangerUsingOrderMethod: createAssociationOrderSetFor " + spec.getFullIdentifier());
067            }
068            
069            // ... and the ordering of the properties and collections
070            final FieldOrderFacet fieldOrderFacet = spec.getFacet(FieldOrderFacet.class);
071            String fieldOrder = fieldOrderFacet == null ? null : fieldOrderFacet.value();
072    
073            if (fieldOrder == null) {
074                fieldOrder = invokeSortOrderMethod(spec, FIELD_PREFIX);
075            }
076            return createOrderSet(fieldOrder, associationMethods);
077        }
078    
079    
080        // ////////////////////////////////////////////////////////////////////////////
081        // actions
082        // ////////////////////////////////////////////////////////////////////////////
083        
084        @Override
085        public OrderSet createActionOrderSetFor(ObjectSpecification spec, List<FacetedMethod> actionFacetedMethodList) {
086            if (LOG.isDebugEnabled()) {
087                LOG.debug("MemberLayoutArrangerUsingOrderMethod: createAssociationOrderSetFor " + spec.getFullIdentifier());
088            }
089    
090            final ActionOrderFacet actionOrderFacet = spec.getFacet(ActionOrderFacet.class);
091            String actionOrder = actionOrderFacet == null ? null : actionOrderFacet.value();
092            if (actionOrder == null) {
093                actionOrder = invokeSortOrderMethod(spec, ACTION_PREFIX);
094            }
095            return createOrderSet(actionOrder, actionFacetedMethodList);
096        }
097        
098        // ////////////////////////////////////////////////////////////////////////////
099        // helpers
100        // ////////////////////////////////////////////////////////////////////////////
101    
102        /**
103         * Invokes a method called <tt>xxxOrder()</tt>, returning a {@link String}.
104         * @param spec 
105         */
106        private String invokeSortOrderMethod(ObjectSpecification spec, final String methodNamePrefix) {
107            final List<Method> methods = Arrays.asList(spec.getCorrespondingClass().getMethods());
108            final Method method = MethodFinderUtils.findMethod(methods, MethodScope.CLASS, (methodNamePrefix + "Order"), String.class, NO_PARAMETERS_TYPES);
109            if (method == null) {
110                return null;
111            } 
112            
113            if (!JavaClassUtils.isStatic(method)) {
114                LOG.warn("method " + spec.getFullIdentifier() + "." + methodNamePrefix + "Order() must be declared as static");
115                return null;
116            } 
117            
118            try {
119                String s = (String) method.invoke(null, NO_PARAMETERS);
120                if (StringUtils.isNullOrEmpty(s)) {
121                    return null;
122                }
123                return s;
124            } catch (IllegalArgumentException e) {
125                LOG.warn("method " + spec.getFullIdentifier() + "#" + method.getName() + "() should accept no parameters");
126                return null;
127            } catch (IllegalAccessException e) {
128                LOG.warn("method " + spec.getFullIdentifier() + "#" + method.getName() + "() must be declared as public");
129                return null;
130            } catch (InvocationTargetException e) {
131                LOG.warn("method " + spec.getFullIdentifier() + "#" + method.getName() + "() has thrown an exception");
132                return null;
133            }
134        }
135    
136        private OrderSet createOrderSet(final String order, final List<FacetedMethod> members) {
137            if (order == null) {
138                return null;
139            } 
140            return SimpleOrderSet.createOrderSet(order, members);
141        }
142    }