/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.isis.core.metamodel.layoutmetadata.json; import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.isis.applib.annotation.MemberGroupLayout.ColumnSpans; import org.apache.isis.applib.annotation.Render; import org.apache.isis.applib.annotation.Render.Type; import org.apache.isis.applib.annotation.When; import org.apache.isis.applib.annotation.Where; import org.apache.isis.applib.filter.Filter; import org.apache.isis.applib.services.grid.GridService; import org.apache.isis.core.commons.lang.ClassExtensions; import org.apache.isis.core.metamodel.facets.all.describedas.DescribedAsFacet; import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet; import org.apache.isis.core.metamodel.facets.all.named.NamedFacet; import org.apache.isis.core.metamodel.facets.collections.collection.defaultview.DefaultViewFacet; import org.apache.isis.core.metamodel.facets.members.cssclass.CssClassFacet; import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacet; import org.apache.isis.core.metamodel.facets.members.disabled.DisabledFacetAbstractImpl; import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet; import org.apache.isis.core.metamodel.facets.members.render.RenderFacet; import org.apache.isis.core.metamodel.facets.object.membergroups.MemberGroupLayoutFacet; import org.apache.isis.core.metamodel.facets.object.paged.PagedFacet; import org.apache.isis.core.metamodel.facets.objectvalue.multiline.MultiLineFacet; import org.apache.isis.core.metamodel.facets.objectvalue.typicallen.TypicalLengthFacet; import org.apache.isis.core.metamodel.layout.memberorderfacet.MemberOrderFacetComparator; import org.apache.isis.core.metamodel.layoutmetadata.ActionLayoutFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.ActionRepr; import org.apache.isis.core.metamodel.layoutmetadata.CollectionLayoutFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.ColumnRepr; import org.apache.isis.core.metamodel.layoutmetadata.CssClassFaFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.CssClassFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.DefaultViewFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.DescribedAsFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.DisabledFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.HiddenFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.LayoutMetadata; import org.apache.isis.core.metamodel.layoutmetadata.LayoutMetadataReader2; import org.apache.isis.core.metamodel.layoutmetadata.MemberGroupRepr; import org.apache.isis.core.metamodel.layoutmetadata.MemberRepr; import org.apache.isis.core.metamodel.layoutmetadata.MultiLineFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.NamedFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.PagedFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.PropertyLayoutFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.RenderFacetRepr; import org.apache.isis.core.metamodel.layoutmetadata.TypicalLengthFacetRepr; import org.apache.isis.core.metamodel.services.ServicesInjector; import org.apache.isis.core.metamodel.services.ServicesInjectorAware; import org.apache.isis.core.metamodel.services.grid.fixedcols.applib.Hint; import org.apache.isis.core.metamodel.spec.ActionType; import org.apache.isis.core.metamodel.spec.ObjectSpecification; import org.apache.isis.core.metamodel.spec.ObjectSpecifications; import org.apache.isis.core.metamodel.spec.feature.Contributed; import org.apache.isis.core.metamodel.spec.feature.ObjectAction; import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; public class LayoutMetadataReaderFromJson implements LayoutMetadataReader2, ServicesInjectorAware { private static final Logger LOG = LoggerFactory.getLogger(LayoutMetadataReaderFromJson.class); @Override public Support support() { return Support.entitiesOnly(); } public Properties asProperties(final Class<?> domainClass) { final LayoutMetadata metadata = readMetadata(domainClass); if(metadata == null) { return null; } if(metadata.getColumns() == null || metadata.getColumns().size() != 4) { throw new ReaderException("JSON metadata must have precisely 4 columns (prop/prop/prop/coll)"); } final Properties props = new Properties(); setMemberGroupLayoutColumnSpans(metadata, props); setMemberGroupLayoutColumnLists(metadata, 0, "left", props); setMemberGroupLayoutColumnLists(metadata, 1, "middle", props); setMemberGroupLayoutColumnLists(metadata, 2, "right", props); final int[] memberSeq = {0}; setProperties(metadata, props, memberSeq); setCollections(metadata, props, memberSeq); setFreestandingActions(metadata, props); return props; } private static void setMemberGroupLayoutColumnSpans(final LayoutMetadata metadata, final Properties props) { final List<ColumnRepr> columns = metadata.getColumns(); final String columnSpansStr = Joiner.on(",").join(Iterables.transform(columns, new Function<ColumnRepr,Integer>(){ @Override public Integer apply(final ColumnRepr input) { return input.span; }})); props.setProperty("class.memberGroupLayout.columnSpans", columnSpansStr); } private static void setMemberGroupLayoutColumnLists(final LayoutMetadata metadata, final int colIdx, final String propkey, final Properties props) { final ColumnRepr column = metadata.getColumns().get(colIdx); final Map<String, MemberGroupRepr> memberGroups = column.memberGroups; final String val = memberGroups != null ? Joiner.on(",").join(memberGroups.keySet()) : ""; props.setProperty("class.memberGroupLayout." + propkey, val); } private static void setProperties(final LayoutMetadata metadata, final Properties props, final int[] memberSeq) { final List<ColumnRepr> columns = metadata.getColumns(); for (final ColumnRepr columnRepr : columns) { final Map<String, MemberGroupRepr> memberGroups = columnRepr.memberGroups; if(memberGroups == null) { continue; } for (final String memberGroupName : memberGroups.keySet()) { final MemberGroupRepr memberGroup = memberGroups.get(memberGroupName); final Map<String, MemberRepr> members = memberGroup.members; if(members == null) { continue; } setMembersAndAssociatedActions(props, memberGroupName, members, memberSeq); } } } private static void setCollections(final LayoutMetadata metadata, final Properties props, final int[] memberSeq) { final ColumnRepr columnRepr = metadata.getColumns().get(3); final Map<String, MemberRepr> collections = columnRepr.collections; setMembersAndAssociatedActions(props, null, collections, memberSeq); } private static void setMembersAndAssociatedActions(final Properties props, final String memberGroupName, final Map<String, MemberRepr> members, final int[] memberSeq) { for(final String memberName: members.keySet()) { props.setProperty("member." + memberName + ".memberOrder.sequence", ""+ ++memberSeq[0]); if(memberGroupName != null) { props.setProperty("member." + memberName + ".memberOrder.name", memberGroupName); } final MemberRepr memberRepr = members.get(memberName); // actions final Map<String, ActionRepr> actions = memberRepr.actions; if(actions != null) { int actSeq = 0; for(final String actionName: actions.keySet()) { final ActionRepr actionRepr = actions.get(actionName); final String nameKey = "action." + actionName + ".memberOrder.name"; props.setProperty(nameKey, memberName); setRemainingActionProperties(props, "action", actionName, actionRepr, ++actSeq); } } // propertyLayout final PropertyLayoutFacetRepr propertyLayout = memberRepr.propertyLayout; if(propertyLayout!= null) { if(propertyLayout.cssClass != null) { props.setProperty("member." + memberName + ".propertyLayout.cssClass", propertyLayout.cssClass); } if(propertyLayout.describedAs != null) { props.setProperty("member." + memberName + ".propertyLayout.describedAs", propertyLayout.describedAs); } if(propertyLayout.hidden != null) { props.setProperty("member." + memberName + ".propertyLayout.hidden", ""+propertyLayout.hidden); } if(propertyLayout.labelPosition != null) { props.setProperty("member." + memberName + ".propertyLayout.labelPosition", ""+propertyLayout.labelPosition); } if(propertyLayout.multiLine > 1) { props.setProperty("member." + memberName + ".propertyLayout.multiLine", "" + propertyLayout.multiLine); } if(propertyLayout.named != null) { props.setProperty("member." + memberName + ".propertyLayout.named", propertyLayout.named); } // { props.setProperty("member." + memberName + ".propertyLayout.namedEscaped", ""+propertyLayout.namedEscaped); } // { props.setProperty("member." + memberName + ".propertyLayout.renderedAsDayBefore", "" + propertyLayout.renderedAsDayBefore); } if(propertyLayout.typicalLength > 0) { props.setProperty("member." + memberName + ".propertyLayout.typicalLength", "" + propertyLayout.typicalLength); } } // collectionLayout final CollectionLayoutFacetRepr collectionLayout = memberRepr.collectionLayout; if(collectionLayout!= null) { if(collectionLayout.cssClass != null) { props.setProperty("member." + memberName + ".collectionLayout.cssClass", collectionLayout.cssClass); } if(collectionLayout.describedAs != null) { props.setProperty("member." + memberName + ".collectionLayout.describedAs", collectionLayout.describedAs); } if(collectionLayout.defaultView != null) { props.setProperty("member." + memberName + ".collectionLayout.defaultView", collectionLayout.defaultView); } if(collectionLayout.hidden != null) { props.setProperty("member." + memberName + ".collectionLayout.hidden", ""+collectionLayout.hidden); } if(collectionLayout.named != null) { props.setProperty("member." + memberName + ".collectionLayout.named", collectionLayout.named); } // { props.setProperty("member." + memberName + ".collectionLayout.namedEscaped", ""+collectionLayout.namedEscaped); } if(collectionLayout.paged > 0) { props.setProperty("member." + memberName + ".collectionLayout.paged", "" + collectionLayout.paged); } if(collectionLayout.render != null) { props.setProperty("member." + memberName + ".collectionLayout.render", ""+collectionLayout.render); } if(collectionLayout.sortedBy != null) { props.setProperty("member." + memberName + ".collectionLayout.sortedBy", collectionLayout.sortedBy); } } // deprecated - properties & collections final CssClassFacetRepr cssClass = memberRepr.cssClass; if(cssClass!= null) { props.setProperty("member." + memberName + ".cssClass.value", cssClass.value); } final DescribedAsFacetRepr describedAs = memberRepr.describedAs; if(describedAs!= null) { props.setProperty("member." + memberName + ".describedAs.value", describedAs.value); } final DisabledFacetRepr disabled = memberRepr.disabled; if(disabled != null) { // same default as in Disabled.when() final When disabledWhen = disabled.when!=null?disabled.when: When.ALWAYS; props.setProperty("member." + memberName + ".disabled.when", disabledWhen.toString()); // same default as in Disabled.where() final Where disabledWhere = disabled.where!=null?disabled.where: Where.ANYWHERE; props.setProperty("member." + memberName + ".disabled.where", disabledWhere.toString()); // same default as in Disabled.reason() final String disabledReason = disabled.reason!=null?disabled.reason: ""; props.setProperty("member." + memberName + ".disabled.reason", disabledReason); } final HiddenFacetRepr hidden = memberRepr.hidden; if(hidden != null) { // same default as in Hidden.when() final When hiddenWhen = hidden.when!=null?hidden.when: When.ALWAYS; props.setProperty("member." + memberName + ".hidden.when", hiddenWhen.toString()); // same default as in Hidden.where() final Where hiddenWhere = hidden.where!=null?hidden.where: Where.ANYWHERE; props.setProperty("member." + memberName + ".hidden.where", hiddenWhere.toString()); } final NamedFacetRepr named = memberRepr.named; if(named != null) { props.setProperty("member." + memberName + ".named.value", named.value); } // deprecated - properties final MultiLineFacetRepr multiLine = memberRepr.multiLine; if(multiLine!= null) { props.setProperty("member." + memberName + ".multiLine.numberOfLines", ""+multiLine.numberOfLines); } final TypicalLengthFacetRepr typicalLength = memberRepr.typicalLength; if(typicalLength!= null) { props.setProperty("member." + memberName + ".typicalLength.value", ""+typicalLength.value); } // deprecated - collections final PagedFacetRepr paged = memberRepr.paged; if(paged != null) { props.setProperty("member." + memberName + ".paged.value", ""+paged.value); } final RenderFacetRepr render = memberRepr.render; if(render != null) { // same default as in Render.Type.value() final Type renderType = render.value!=null?render.value: Render.Type.EAGERLY; props.setProperty("member." + memberName + ".render.value", renderType.toString()); } } } private static void setFreestandingActions(final LayoutMetadata metadata, final Properties props) { if(metadata.getActions() == null) { return; } int xeq=0; final Map<String, ActionRepr> actions = metadata.getActions(); for (final String actionName : actions.keySet()) { final ActionRepr actionRepr = actions.get(actionName); setRemainingActionProperties(props, "member", actionName, actionRepr, ++xeq); } } private static void setRemainingActionProperties( final Properties props, final String prefix, final String actionNameOrig, final ActionRepr actionRepr, final int seq) { final String actionName = actionNameOrig + ("action".equals(prefix)?"":"()"); props.setProperty(prefix + "." + actionName + ".memberOrder.sequence", ""+ seq); final ActionLayoutFacetRepr actionLayout = actionRepr.actionLayout; if(actionLayout != null) { if(actionLayout.bookmarking != null) { props.setProperty(prefix + "." + actionName + ".actionLayout.bookmarking", ""+actionLayout.bookmarking); } if(actionLayout.cssClass != null) { props.setProperty(prefix + "." + actionName + ".actionLayout.cssClass", actionLayout.cssClass); } if(actionLayout.cssClassFa != null) { props.setProperty(prefix + "." + actionName + ".actionLayout.cssClassFa", actionLayout.cssClassFa); } if(actionLayout.cssClassFaPosition != null) { props.setProperty(prefix + "." + actionName + ".actionLayout.cssClassFaPosition", actionLayout.cssClassFaPosition); } if(actionLayout.describedAs != null) { props.setProperty(prefix + "." + actionName + ".actionLayout.describedAs", actionLayout.describedAs); } if(actionLayout.hidden != null) { props.setProperty(prefix + "." + actionName + ".actionLayout.hidden", ""+actionLayout.hidden); } if(actionLayout.named != null) { props.setProperty(prefix + "." + actionName + ".actionLayout.named", actionLayout.named); } // { props.setProperty(prefix + "." + actionName + ".actionLayout.namedEscaped", ""+actionLayout.namedEscaped); } if(actionLayout.position != null) { props.setProperty(prefix + "." + actionName + ".actionLayout.position", ""+actionLayout.position); } } // deprecated final CssClassFacetRepr cssClass = actionRepr.cssClass; if(cssClass!= null) { props.setProperty(prefix +"." + actionName + ".cssClass.value", cssClass.value); } final CssClassFaFacetRepr cssClassFa = actionRepr.cssClassFa; if(cssClassFa != null) { props.setProperty(prefix +"." + actionName + ".cssClassFa.value", cssClassFa.value); props.setProperty(prefix +"." + actionName + ".cssClassFa.position", cssClassFa.position); } final DescribedAsFacetRepr describedAs = actionRepr.describedAs; if(describedAs!= null) { props.setProperty(prefix +"." + actionName + ".describedAs.value", describedAs.value); } final NamedFacetRepr actionNamed = actionRepr.named; if(actionNamed != null) { props.setProperty(prefix +"." + actionName + ".named.value", actionNamed.value); } } public LayoutMetadata asLayoutMetadata(final Class<?> domainClass) { return readMetadata(domainClass); } // ////////////////////////////////////// private final Set<Class<?>> blacklisted = Sets.newConcurrentHashSet(); private LayoutMetadata readMetadata(final Class<?> domainClass) { final String content; if(blacklisted.contains(domainClass)) { return null; } try { final GridService gridService = getGridService(); if(gridService.existsFor(domainClass)) { blacklisted.add(domainClass); return null; } } catch (IllegalArgumentException ex) { // ignore } final String resourceName = domainClass.getSimpleName() + ".layout.json"; try { content = ClassExtensions.resourceContentOf(domainClass, resourceName); } catch (IOException | IllegalArgumentException ex) { blacklisted.add(domainClass); final String message = String .format( "Failed to locate file %s (relative to %s.class); ex: %s)", resourceName, domainClass.getName(), ex.getMessage()); LOG.debug(message); return null; } try { return readMetadata(content); } catch(Exception ex) { // note that we don't blacklist if the file exists but couldn't be parsed; // the developer might fix so we will want to retry. final String message = "Failed to parse " + domainClass.getName() + ".layout.json file (" + ex.getMessage() + ")"; LOG.warn(message); return null; } } LayoutMetadata readMetadata(final String content) { final Gson gson = new GsonBuilder().create(); return gson.fromJson(content, LayoutMetadata.class); } // ////////////////////////////////////// private final static MemberOrderFacetComparator memberOrderFacetComparator = new MemberOrderFacetComparator(false); /** * not API */ public String asJson(final ObjectSpecification objectSpec) { final LayoutMetadata metadata = new LayoutMetadata(); metadata.setColumns(Lists.<ColumnRepr>newArrayList()); final MemberGroupLayoutFacet mglf = objectSpec.getFacet(MemberGroupLayoutFacet.class); final ColumnSpans columnSpans = mglf.getColumnSpans(); final Set<String> actionIdsForAssociations = Sets.newTreeSet(); ColumnRepr columnRepr; columnRepr = addColumnWithSpan(metadata, columnSpans.getLeft()); updateColumnMemberGroups(objectSpec, Hint.LEFT, columnRepr, actionIdsForAssociations); columnRepr = addColumnWithSpan(metadata, columnSpans.getMiddle()); updateColumnMemberGroups(objectSpec, Hint.MIDDLE, columnRepr, actionIdsForAssociations); columnRepr = addColumnWithSpan(metadata, columnSpans.getRight()); updateColumnMemberGroups(objectSpec, Hint.RIGHT, columnRepr, actionIdsForAssociations); columnRepr = addColumnWithSpan(metadata, columnSpans.getCollections()); updateCollectionColumnRepr(objectSpec, columnRepr, actionIdsForAssociations); addActions(objectSpec, metadata, actionIdsForAssociations); final Gson gson = new GsonBuilder().setPrettyPrinting().create(); return gson.toJson(metadata); } private static void updateColumnMemberGroups(final ObjectSpecification objectSpec, final Hint hint, final ColumnRepr columnRepr, final Set<String> actionIdsForAssociations) { final List<ObjectAssociation> objectAssociations = propertiesOf(objectSpec); final Map<String, List<ObjectAssociation>> associationsByGroup = ObjectAssociation.Util.groupByMemberOrderName(objectAssociations ); final List<String> groupNames = ObjectSpecifications.orderByMemberGroups(objectSpec, associationsByGroup.keySet(), hint); columnRepr.memberGroups = Maps.newLinkedHashMap(); for (final String groupName : groupNames) { final MemberGroupRepr memberGroupRepr = new MemberGroupRepr(); columnRepr.memberGroups.put(groupName, memberGroupRepr); final List<ObjectAssociation> associationsInGroup = associationsByGroup.get(groupName); memberGroupRepr.members = Maps.newLinkedHashMap(); if(associationsInGroup == null) { continue; } for (final ObjectAssociation assoc : associationsInGroup) { final MemberRepr memberRepr = newMemberRepr(objectSpec, assoc, actionIdsForAssociations); memberGroupRepr.members.put(assoc.getId(), memberRepr); } } } private static void addActions(final ObjectSpecification objectSpec, final LayoutMetadata metadata, final Set<String> actionIdsForAssociations) { final Map<String, ActionRepr> actions = Maps.newLinkedHashMap(); final List<ObjectAction> actionsOf = actionsOf(objectSpec, actionIdsForAssociations); for(final ObjectAction action: actionsOf) { actions.put(action.getId(), newActionRepr(objectSpec, action)); } metadata.setActions(actions); } private static ActionRepr newActionRepr(final ObjectSpecification objectSpec, final ObjectAction action) { final ActionRepr actionRepr = new ActionRepr(); final CssClassFacet cssClassFacet = action.getFacet(CssClassFacet.class); if(cssClassFacet != null && !cssClassFacet.isNoop()) { final CssClassFacetRepr cssClassFacetRepr = new CssClassFacetRepr(); cssClassFacetRepr.value = cssClassFacet.cssClass(null); actionRepr.cssClass = cssClassFacetRepr; } final DescribedAsFacet describedAsFacet = action.getFacet(DescribedAsFacet.class); if(describedAsFacet != null && !describedAsFacet.isNoop() && !Strings.isNullOrEmpty(describedAsFacet.value())) { final DescribedAsFacetRepr describedAsFacetRepr = new DescribedAsFacetRepr(); describedAsFacetRepr.value = describedAsFacet.value(); actionRepr.describedAs = describedAsFacetRepr; } final NamedFacet namedFacet = action.getFacet(NamedFacet.class); if(namedFacet != null && !namedFacet.isNoop()) { final NamedFacetRepr namedFacetRepr = new NamedFacetRepr(); namedFacetRepr.value = namedFacet.value(); actionRepr.named = namedFacetRepr; } return actionRepr; } private static void updateCollectionColumnRepr(final ObjectSpecification objectSpec, final ColumnRepr columnRepr, final Set<String> actionIdsOfAssociations) { final List<ObjectAssociation> objectAssociations = collectionsOf(objectSpec); columnRepr.collections = Maps.newLinkedHashMap(); for(final ObjectAssociation assoc: objectAssociations) { final MemberRepr memberRepr = newMemberRepr(objectSpec, assoc, actionIdsOfAssociations); columnRepr.collections.put(assoc.getId(), memberRepr); } } private static MemberRepr newMemberRepr(final ObjectSpecification objectSpec, final ObjectAssociation assoc, final Set<String> actionIdsForAssociations) { final MemberRepr memberRepr = new MemberRepr(); final CssClassFacet cssClassFacet = assoc.getFacet(CssClassFacet.class); if(cssClassFacet != null && !cssClassFacet.isNoop()) { final CssClassFacetRepr cssClassFacetRepr = new CssClassFacetRepr(); cssClassFacetRepr.value = cssClassFacet.cssClass(null); memberRepr.cssClass = cssClassFacetRepr; } final DescribedAsFacet describedAsFacet = assoc.getFacet(DescribedAsFacet.class); if(describedAsFacet != null && !describedAsFacet.isNoop() && !Strings.isNullOrEmpty(describedAsFacet.value())) { final DescribedAsFacetRepr describedAsFacetRepr = new DescribedAsFacetRepr(); describedAsFacetRepr.value = describedAsFacet.value(); memberRepr.describedAs = describedAsFacetRepr; } final DefaultViewFacet defaultViewFacet = assoc.getFacet(DefaultViewFacet.class); if(defaultViewFacet != null && !defaultViewFacet.isNoop() && !Strings.isNullOrEmpty(defaultViewFacet.value())) { final DefaultViewFacetRepr defaultViewFacetRepr = new DefaultViewFacetRepr(); defaultViewFacetRepr.value = describedAsFacet.value(); } final NamedFacet namedFacet = assoc.getFacet(NamedFacet.class); if(namedFacet != null && !namedFacet.isNoop()) { final NamedFacetRepr namedFacetRepr = new NamedFacetRepr(); namedFacetRepr.value = namedFacet.value(); memberRepr.named = namedFacetRepr; } final DisabledFacet disabledFacet = assoc.getFacet(DisabledFacet.class); if(disabledFacet != null && !disabledFacet.isNoop()) { final DisabledFacetRepr disabledFacetRepr = new DisabledFacetRepr(); if(disabledFacet instanceof DisabledFacetAbstractImpl) { final DisabledFacetAbstractImpl disabledFacetImpl = (DisabledFacetAbstractImpl) disabledFacet; disabledFacetRepr.reason = Strings.emptyToNull(disabledFacetImpl.getReason()); } disabledFacetRepr.when = whenAlwaysToNull(disabledFacet.when()); disabledFacetRepr.where = whereAnywhereToNull(disabledFacet.where()); memberRepr.disabled = disabledFacetRepr; } // relies on the fact that HiddenFacetAbstract is multi-typed final HiddenFacet hiddenFacet = assoc.getFacet(HiddenFacet.class); if(hiddenFacet != null && !hiddenFacet.isNoop()) { final HiddenFacetRepr hiddenFacetRepr = new HiddenFacetRepr(); hiddenFacetRepr.when = whenAlwaysToNull(hiddenFacet.when()); hiddenFacetRepr.where = whereAnywhereToNull(hiddenFacet.where()); memberRepr.hidden = hiddenFacetRepr; } final MultiLineFacet multiLineFacet = assoc.getFacet(MultiLineFacet.class); if(multiLineFacet != null && !multiLineFacet.isNoop()) { final MultiLineFacetRepr multiLineFacetRepr = new MultiLineFacetRepr(); multiLineFacetRepr.numberOfLines = multiLineFacet.numberOfLines(); memberRepr.multiLine = multiLineFacetRepr; } final PagedFacet pagedFacet = assoc.getFacet(PagedFacet.class); if(pagedFacet != null && !pagedFacet.isNoop()) { final PagedFacetRepr pagedFacetRepr = new PagedFacetRepr(); pagedFacetRepr.value = pagedFacet.value(); memberRepr.paged = pagedFacetRepr; } final RenderFacet renderFacet = assoc.getFacet(RenderFacet.class); if(renderFacet != null && !renderFacet.isNoop()) { final RenderFacetRepr renderFacetRepr = new RenderFacetRepr(); renderFacetRepr.value = renderFacet.value(); memberRepr.render = renderFacetRepr; } final TypicalLengthFacet typicalLengthFacet = assoc.getFacet(TypicalLengthFacet.class); if(typicalLengthFacet != null && !typicalLengthFacet.isNoop()) { final TypicalLengthFacetRepr typicalLengthFacetRepr = new TypicalLengthFacetRepr(); typicalLengthFacetRepr.value = typicalLengthFacet.value(); memberRepr.typicalLength = typicalLengthFacetRepr; } final List<ObjectAction> actions = objectSpec.getObjectActions( ActionType.USER, Contributed.INCLUDED, ObjectAction.Filters.memberOrderOf(assoc)); if(!actions.isEmpty()) { memberRepr.actions = Maps.newLinkedHashMap(); sortByMemberOrderFacet(actions); for (final ObjectAction action : actions) { final String actionId = action.getId(); memberRepr.actions.put(actionId, new ActionRepr()); actionIdsForAssociations.add(actionId); } } return memberRepr; } private static Where whereAnywhereToNull(final Where where) { return where != Where.ANYWHERE? where: null; } private static When whenAlwaysToNull(final When when) { return when != When.ALWAYS? when: null; } private static void sortByMemberOrderFacet(final List<ObjectAction> actions) { Collections.sort(actions, new Comparator<ObjectAction>() { @Override public int compare(final ObjectAction o1, final ObjectAction o2) { final MemberOrderFacet m1 = o1.getFacet(MemberOrderFacet.class); final MemberOrderFacet m2 = o2.getFacet(MemberOrderFacet.class); return memberOrderFacetComparator.compare(m1, m2); }}); } private static ColumnRepr addColumnWithSpan(final LayoutMetadata metadata, final int span) { final ColumnRepr col = new ColumnRepr(); metadata.getColumns().add(col); col.span = span; return col; } private static List<ObjectAssociation> propertiesOf(final ObjectSpecification objSpec) { return objSpec.getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.PROPERTIES); } private static List<ObjectAssociation> collectionsOf(final ObjectSpecification objSpec) { return objSpec.getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.COLLECTIONS); } private static List<ObjectAction> actionsOf(final ObjectSpecification objSpec, final Set<String> excludedActionIds) { return objSpec.getObjectActions(ActionType.ALL, Contributed.INCLUDED, excluding(excludedActionIds)); } @SuppressWarnings({ "deprecation" }) private static Filter<ObjectAction> excluding(final Set<String> excludedActionIds) { return new Filter<ObjectAction>(){ @Override public boolean accept(final ObjectAction t) { return !excludedActionIds.contains(t.getId()); } }; } @Override public String toString() { return getClass().getName(); } private GridService getGridService() { return servicesInjector.lookupService(GridService.class); } private ServicesInjector servicesInjector; @Override public void setServicesInjector(final ServicesInjector servicesInjector) { this.servicesInjector = servicesInjector; } }