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 */
017package org.apache.isis.core.metamodel.services.devutils;
018
019import java.util.Arrays;
020import java.util.List;
021import java.util.SortedSet;
022
023import com.google.common.base.Joiner;
024import com.google.common.base.Strings;
025import com.google.common.collect.Sets;
026
027import org.apache.isis.applib.util.ObjectContracts;
028import org.apache.isis.core.commons.lang.StringExtensions;
029import org.apache.isis.core.metamodel.facetapi.Facet;
030import org.apache.isis.core.metamodel.facets.ImperativeFacet;
031import org.apache.isis.core.metamodel.facets.param.choices.ActionChoicesFacet;
032import org.apache.isis.core.metamodel.facets.actions.defaults.ActionDefaultsFacet;
033import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
034import org.apache.isis.core.metamodel.facets.param.autocomplete.ActionParameterAutoCompleteFacet;
035import org.apache.isis.core.metamodel.facets.param.choices.ActionParameterChoicesFacet;
036import org.apache.isis.core.metamodel.facets.param.defaults.ActionParameterDefaultsFacet;
037import org.apache.isis.core.metamodel.facets.properties.autocomplete.PropertyAutoCompleteFacet;
038import org.apache.isis.core.metamodel.facets.properties.choices.PropertyChoicesFacet;
039import org.apache.isis.core.metamodel.facets.properties.defaults.PropertyDefaultFacet;
040import org.apache.isis.core.metamodel.spec.ObjectSpecification;
041import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
042import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
043import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
044import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
045import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
046import org.apache.isis.core.metamodel.facets.actions.validate.ActionValidationFacet;
047import org.apache.isis.core.metamodel.facets.collections.validate.CollectionValidateAddToFacet;
048import org.apache.isis.core.metamodel.facets.collections.validate.CollectionValidateRemoveFromFacet;
049import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet;
050import org.apache.isis.core.metamodel.facets.properties.validating.PropertyValidateFacet;
051
052public class MetaModelRow implements Comparable<MetaModelRow>{
053
054    enum MemberType {
055        PROPERTY,
056        COLLECTION,
057        ACTION;
058    }
059
060    private final ObjectSpecification spec;
061    private final MemberType memberType;
062    private final ObjectMember member;
063    private ObjectAction action;
064    
065    MetaModelRow(ObjectSpecification spec, OneToOneAssociation property) {
066        this.spec = spec;
067        this.member = property;
068        this.memberType = MemberType.PROPERTY;
069    }
070
071    MetaModelRow(ObjectSpecification spec, OneToManyAssociation collection) {
072        this.spec = spec;
073        this.member = collection;
074        this.memberType = MemberType.COLLECTION;
075    }
076    
077    MetaModelRow(ObjectSpecification spec, ObjectAction action) {
078        this.spec = spec;
079        this.member = this.action = action;
080        this.memberType = MemberType.ACTION;
081    }
082
083    public String getClassType() {
084        boolean service = false;
085        for(ObjectSpecification subspecs: spec.subclasses()) {
086            service = service || subspecs.isService();
087        }
088        return service || spec.isService() ?"2 Service":spec.isValue()?"3 Value":spec.isParentedOrFreeCollection()?"4 Collection":"1 Object";
089    }
090    public String getClassName() {
091        final String fullIdentifier = spec.getFullIdentifier();
092        final int lastDot = fullIdentifier.lastIndexOf(".");
093        return lastDot>0 && lastDot < fullIdentifier.length()-1
094                ?fullIdentifier.substring(lastDot+1,fullIdentifier.length())
095                :fullIdentifier;
096    }
097    public String getPackageName() {
098        final String fullIdentifier = spec.getFullIdentifier();
099        final int lastDot = fullIdentifier.lastIndexOf(".");
100        return lastDot>0?fullIdentifier.substring(0,lastDot):fullIdentifier;
101    }
102    public String getType() {
103        return memberType.name().toLowerCase();
104    }
105    public String getMemberName() {
106        return member.getId();
107    }
108    public String getNumParams() {
109        return action!=null?""+action.getParameterCount():"";
110    }
111    String getHidden() {
112        return interpret(HiddenFacet.class);
113    }
114    String getDisabled() {
115        return interpret(DisabledFacet.class);
116    }
117    public String getChoices() {
118        if(memberType == MemberType.PROPERTY) {
119            return interpretRowAndFacet(PropertyChoicesFacet.class);
120        } else if(memberType == MemberType.COLLECTION) {
121            return "";
122        } else {
123            final List<ObjectActionParameter> parameters = this.action.getParameters();
124            final SortedSet<String> interpretations = Sets.newTreeSet();
125            for (ObjectActionParameter param : parameters) {
126                final ActionParameterChoicesFacet facet = param.getFacet(ActionParameterChoicesFacet.class);
127                addIfNotEmpty(interpretFacet(facet), interpretations);
128            }
129            return !interpretations.isEmpty()? Joiner.on(";").join(interpretations) : interpretRowAndFacet(ActionChoicesFacet.class);
130        }
131    }
132    public String getAutoComplete() {
133        if(memberType == MemberType.PROPERTY) {
134            return interpretRowAndFacet(PropertyAutoCompleteFacet.class);
135        } else if(memberType == MemberType.COLLECTION) {
136           return "";
137        } else {
138            final List<ObjectActionParameter> parameters = this.action.getParameters();
139            final SortedSet<String> interpretations = Sets.newTreeSet();
140            for (ObjectActionParameter param : parameters) {
141                final ActionParameterAutoCompleteFacet facet = param.getFacet(ActionParameterAutoCompleteFacet.class);
142                addIfNotEmpty(interpretFacet(facet), interpretations);
143            }
144            return !interpretations.isEmpty()? Joiner.on(";").join(interpretations) : "";
145        }
146    }
147    String getDefault() {
148        if(memberType == MemberType.PROPERTY) {
149            return interpretRowAndFacet(PropertyDefaultFacet.class);
150        } else if(memberType == MemberType.COLLECTION) {
151            return "";
152        } else {
153            final List<ObjectActionParameter> parameters = this.action.getParameters();
154            final SortedSet<String> interpretations = Sets.newTreeSet();
155            for (ObjectActionParameter param : parameters) {
156                final ActionParameterDefaultsFacet facet = param.getFacet(ActionParameterDefaultsFacet.class);
157                addIfNotEmpty(interpretFacet(facet), interpretations);
158            }
159            return !interpretations.isEmpty()? Joiner.on(";").join(interpretations) : interpretRowAndFacet(ActionDefaultsFacet.class);
160        }
161    }
162    String getValidate() {
163        if(memberType == MemberType.PROPERTY) {
164            return interpretRowAndFacet(PropertyValidateFacet.class);
165        } else if(memberType == MemberType.COLLECTION) {
166            final SortedSet<String> interpretations = Sets.newTreeSet();
167            addIfNotEmpty(interpretRowAndFacet(CollectionValidateAddToFacet.class), interpretations);
168            addIfNotEmpty(interpretRowAndFacet(CollectionValidateRemoveFromFacet.class), interpretations);
169            return !interpretations.isEmpty()? Joiner.on(";").join(interpretations) : "";
170       } else {
171           return interpretRowAndFacet(ActionValidationFacet.class);
172        }
173    }
174
175    static Object header() {
176        return "classType,packageName,className,memberType,memberName,numParams,hidden,disabled,choices,autoComplete,default,validate";
177    }
178    
179    String asTextCsv() {
180        return Joiner.on(",").join(
181                getClassType(),
182                getPackageName(),
183                getClassName(),
184                getType(),
185                getMemberName(),
186                getNumParams(),
187                getHidden(),
188                getDisabled(),
189                getChoices(),
190                getAutoComplete(),
191                getDefault(),
192                getValidate());
193    }
194 
195    private String interpretRowAndFacet(Class<? extends Facet> facetClass) {
196        final Facet facet = member.getFacet(facetClass);
197        return interpretFacet(facet);
198    }
199    
200    private static void addIfNotEmpty(final String str, final SortedSet<String> set) {
201        if(!Strings.isNullOrEmpty(str)) {
202            set.add(str);
203        }
204    }
205    
206    private String interpret(final Class<? extends Facet> cls) {
207        return interpretFacet(member.getFacet(cls));
208    }
209
210    private static String interpretFacet(final Facet facet) {
211        if (facet == null || facet.isNoop()) {
212            return "";
213        }
214        if (facet instanceof ImperativeFacet) {
215            ImperativeFacet imperativeFacet = (ImperativeFacet) facet;
216            return imperativeFacet.getMethods().get(0).getName();
217        } 
218        final String name = facet.getClass().getSimpleName();
219        if (ignore(name)) {
220            return "";
221        } 
222        final String abbr = StringExtensions.toAbbreviation(name);
223        return abbr.length()>0 ? abbr : name;
224    }
225
226    protected static boolean ignore(final String name) {
227        return Arrays.asList("PropertyValidateFacetDefault","PropertyDefaultFacetDerivedFromDefaultedFacet").contains(name);
228    }
229
230    @Override
231    public int compareTo(MetaModelRow o) {
232        return ObjectContracts.compare(this, o, "classType,className,type desc,memberName");
233    }
234}