001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.isis.core.metamodel.facetdecorator; 021 022import java.text.MessageFormat; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.LinkedHashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.isis.core.commons.components.ApplicationScopedComponent; 032import org.apache.isis.core.commons.debug.DebugBuilder; 033import org.apache.isis.core.metamodel.exceptions.MetaModelException; 034import org.apache.isis.core.metamodel.facetapi.Facet; 035import org.apache.isis.core.metamodel.facetapi.FacetHolder; 036import org.apache.isis.core.metamodel.spec.ObjectSpecification; 037import org.apache.isis.core.metamodel.spec.feature.Contributed; 038import org.apache.isis.core.metamodel.spec.feature.ObjectAction; 039import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter; 040import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation; 041 042public class FacetDecoratorSet implements ApplicationScopedComponent { 043 044 private final Map<Class<? extends Facet>, List<FacetDecorator>> facetDecoratorByFacetType = new HashMap<Class<? extends Facet>, List<FacetDecorator>>(); 045 private final Set<FacetDecorator> facetDecoratorSet = new LinkedHashSet<FacetDecorator>(); 046 047 // //////////////////////////////////////////////////////////// 048 // init, shutdown 049 // //////////////////////////////////////////////////////////// 050 051 @Override 052 public void init() { 053 } 054 055 @Override 056 public void shutdown() { 057 } 058 059 // //////////////////////////////////////////////////////////// 060 // add, get, isEmpty 061 // //////////////////////////////////////////////////////////// 062 063 public void add(final FacetDecorator decorator) { 064 final Class<? extends Facet>[] decoratedFacetTypes = decorator.getFacetTypes(); 065 for (final Class<? extends Facet> decoratedFacetType : decoratedFacetTypes) { 066 getFacetDecoratorList(decoratedFacetType).add(decorator); 067 facetDecoratorSet.add(decorator); 068 } 069 } 070 071 private List<FacetDecorator> getFacetDecoratorList(final Class<? extends Facet> decoratedFacetType) { 072 List<FacetDecorator> facetDecoratorList = facetDecoratorByFacetType.get(decoratedFacetType); 073 if (facetDecoratorList == null) { 074 facetDecoratorList = new ArrayList<FacetDecorator>(); 075 facetDecoratorByFacetType.put(decoratedFacetType, facetDecoratorList); 076 } 077 return facetDecoratorList; 078 } 079 080 public void add(final List<FacetDecorator> decorators) { 081 for (final FacetDecorator decorator : decorators) { 082 add(decorator); 083 } 084 } 085 086 public Set<FacetDecorator> getFacetDecorators() { 087 return Collections.unmodifiableSet(facetDecoratorSet); 088 } 089 090 public boolean isEmpty() { 091 return facetDecoratorByFacetType.isEmpty(); 092 } 093 094 // //////////////////////////////////////////////////////////// 095 // decorate 096 // //////////////////////////////////////////////////////////// 097 098 /** 099 * @param holder 100 */ 101 public void decorate(final ObjectSpecification holder) { 102 decorateAllFacets(holder); 103 for (final ObjectAssociation objectAssociation : holder.getAssociations(Contributed.EXCLUDED)) { 104 this.decorateAllFacets(objectAssociation); 105 } 106 for (final ObjectAction objectAction : holder.getObjectActions(Contributed.EXCLUDED)) { 107 decorateAllFacets(objectAction); 108 final List<ObjectActionParameter> parameters = objectAction.getParameters(); 109 for (final ObjectActionParameter parameter : parameters) { 110 this.decorateAllFacets(parameter); 111 } 112 } 113 } 114 115 private void decorateAllFacets(final FacetHolder holder) { 116 if (isEmpty()) { 117 return; 118 } 119 final Class<? extends Facet>[] facetTypes = holder.getFacetTypes(); 120 for (final Class<? extends Facet> facetType : facetTypes) { 121 final Facet facet = holder.getFacet(facetType); 122 decorateFacet(facet, holder); 123 } 124 } 125 126 /** 127 * REVIEW: the design is a little clumsy here. We want to decorate the 128 * provided {@link Facet}, but its owning {@link FacetHolder holder} turns 129 * out to be a runtime peer (eg <tt>JavaAction</tt>) rather than the 130 * metamodel (eg {@link ObjectAction}). Since we want to decorate the 131 * {@link ObjectAction}, we have to pass it through. 132 */ 133 private void decorateFacet(final Facet facet, final FacetHolder requiredHolder) { 134 final Class<? extends Facet> facetType = facet.facetType(); 135 final Class<? extends Facet> cls = facetType; 136 final List<FacetDecorator> decoratorList = facetDecoratorByFacetType.get(cls); 137 if (decoratorList == null) { 138 return; 139 } 140 for (final FacetDecorator facetDecorator : decoratorList) { 141 final Facet decoratingFacet = facetDecorator.decorate(facet, requiredHolder); 142 if (decoratingFacet == null) { 143 continue; 144 } 145 ensureDecoratorMetContract(facetDecorator, decoratingFacet, facetType, requiredHolder); 146 } 147 } 148 149 private static void ensureDecoratorMetContract(final FacetDecorator facetDecorator, final Facet decoratingFacet, final Class<? extends Facet> facetType, final FacetHolder originalFacetHolder) { 150 if (decoratingFacet.facetType() != facetType) { 151 throw new MetaModelException(MessageFormat.format("Problem with facet decorator '{0}'; inconsistent decorating facetType() for {1}; was {2} but expectected facetType() of {3}", facetDecorator.getClass().getName(), decoratingFacet.getClass().getName(), decoratingFacet.facetType() 152 .getName(), facetType.getName())); 153 } 154 final Facet facetForFacetType = originalFacetHolder.getFacet(decoratingFacet.facetType()); 155 if (facetForFacetType != decoratingFacet) { 156 throw new MetaModelException(MessageFormat.format("Problem with facet decorator '{0}'; has not replaced original facet for facetType() of {1}", facetDecorator.getClass().getName(), facetType.getName())); 157 } 158 } 159 160 // //////////////////////////////////////////////////////////// 161 // debugging 162 // //////////////////////////////////////////////////////////// 163 164 public void debugData(final DebugBuilder str) { 165 str.appendTitle("Facet decorators"); 166 final Set<Class<? extends Facet>> facetTypes = facetDecoratorByFacetType.keySet(); 167 if (facetTypes.size() == 0) { 168 str.append("none"); 169 } else { 170 for (final Class<? extends Facet> cls : facetTypes) { 171 str.appendln(cls.getName(), facetDecoratorByFacetType.get(cls)); 172 } 173 } 174 } 175 176}