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
020 package org.apache.isis.progmodels.dflt;
021
022 import java.util.Collections;
023 import java.util.LinkedHashSet;
024 import java.util.List;
025 import java.util.Set;
026
027 import com.google.common.collect.Lists;
028
029 import org.apache.log4j.Logger;
030
031 import org.apache.isis.core.commons.config.ConfigurationConstants;
032 import org.apache.isis.core.commons.config.InstallerAbstract;
033 import org.apache.isis.core.commons.config.IsisConfiguration;
034 import org.apache.isis.core.commons.factory.InstanceUtil;
035 import org.apache.isis.core.metamodel.facetdecorator.FacetDecorator;
036 import org.apache.isis.core.metamodel.facets.FacetFactory;
037 import org.apache.isis.core.metamodel.layout.MemberLayoutArranger;
038 import org.apache.isis.core.metamodel.progmodel.ProgrammingModel;
039 import org.apache.isis.core.metamodel.specloader.FacetDecoratorInstaller;
040 import org.apache.isis.core.metamodel.specloader.ObjectReflector;
041 import org.apache.isis.core.metamodel.specloader.ObjectReflectorDefault;
042 import org.apache.isis.core.metamodel.specloader.ObjectReflectorInstaller;
043 import org.apache.isis.core.metamodel.specloader.ReflectorConstants;
044 import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutor;
045 import org.apache.isis.core.metamodel.specloader.classsubstitutor.ClassSubstitutorComposite;
046 import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistry;
047 import org.apache.isis.core.metamodel.specloader.collectiontyperegistry.CollectionTypeRegistryDefault;
048 import org.apache.isis.core.metamodel.specloader.traverser.SpecificationTraverser;
049 import org.apache.isis.core.metamodel.specloader.validator.MetaModelValidator;
050 import org.apache.isis.runtimes.dflt.runtime.installerregistry.InstallerLookup;
051 import org.apache.isis.runtimes.dflt.runtime.installerregistry.InstallerLookupAware;
052
053 public class JavaReflectorInstaller extends InstallerAbstract implements ObjectReflectorInstaller, InstallerLookupAware {
054
055 private static final Logger LOG = Logger.getLogger(JavaReflectorInstaller.class);
056
057 public static final String PROPERTY_BASE = ConfigurationConstants.ROOT;
058
059 /**
060 * Defaulted in the constructor.
061 */
062 private final LinkedHashSet<FacetDecoratorInstaller> decoratorInstallers;
063
064 private InstallerLookup installerLookup;
065
066 // /////////////////////////////////////////////////////
067 // Constructor
068 // /////////////////////////////////////////////////////
069
070 public JavaReflectorInstaller() {
071 this("java");
072 }
073
074 public JavaReflectorInstaller(final String name) {
075 super(ObjectReflectorInstaller.TYPE, name);
076 decoratorInstallers = new LinkedHashSet<FacetDecoratorInstaller>();
077 }
078
079 // /////////////////////////////////////////////////////
080 // createReflector, doCreateReflector
081 // /////////////////////////////////////////////////////
082
083 /**
084 * Should call
085 * {@link #addFacetDecoratorInstaller(ReflectorDecoratorInstaller)} prior to
086 * calling this.
087 */
088 @Override
089 public ObjectReflectorDefault createReflector() {
090 final ClassSubstitutor classSubstitutor = createClassSubstitutor(getConfiguration());
091 final CollectionTypeRegistry collectionTypeRegistry = createCollectionTypeRegistry(getConfiguration());
092 final SpecificationTraverser specificationTraverser = createSpecificationTraverser(getConfiguration());
093 final MemberLayoutArranger memberLayoutArranger = createMemberLayoutArranger(getConfiguration());
094 final ProgrammingModel programmingModel = createProgrammingModelFacets(getConfiguration());
095 final Set<FacetDecorator> facetDecorators = createFacetDecorators(getConfiguration());
096 final MetaModelValidator metaModelValidator = createMetaModelValidator(getConfiguration());
097
098 final ObjectReflectorDefault reflector = doCreateReflector(getConfiguration(), classSubstitutor, collectionTypeRegistry, specificationTraverser, memberLayoutArranger, programmingModel, facetDecorators, metaModelValidator);
099
100 return reflector;
101 }
102
103 /**
104 * Hook method to allow subclasses to specify a different implementation of
105 * {@link ClassSubstitutor}.
106 *
107 * <p>
108 * By default, looks up implementation from provided
109 * {@link IsisConfiguration} using
110 * {@link ReflectorConstants#CLASS_SUBSTITUTOR_CLASS_NAME_LIST}. If not
111 * specified, then defaults to
112 * {@value ReflectorConstants#CLASS_SUBSTITUTOR_CLASS_NAME_DEFAULT}.
113 *
114 * <p>
115 *
116 */
117 protected ClassSubstitutor createClassSubstitutor(final IsisConfiguration configuration) {
118 final String[] configuredClassNames = configuration.getList(ReflectorConstants.CLASS_SUBSTITUTOR_CLASS_NAME_LIST);
119 if (configuredClassNames == null || configuredClassNames.length == 0) {
120 return InstanceUtil.createInstance(ReflectorConstants.CLASS_SUBSTITUTOR_CLASS_NAME_DEFAULT, ClassSubstitutor.class);
121 }
122 final List<ClassSubstitutor> substitutors = Lists.newArrayList();
123 for (final String className : configuredClassNames) {
124 final ClassSubstitutor substitutor = InstanceUtil.createInstance(className, ClassSubstitutor.class);
125 substitutors.add(substitutor);
126 }
127 return substitutors.size() == 1 ? substitutors.get(0) : new ClassSubstitutorComposite(substitutors);
128 }
129
130 /**
131 * Hook method to allow subclasses to specify a different implementation of
132 * {@link SpecificationTraverser}.
133 *
134 * <p>
135 * By default, looks up implementation from provided
136 * {@link IsisConfiguration} using
137 * {@link ReflectorConstants#SPECIFICATION_TRAVERSER_CLASS_NAME}. If not
138 * specified, then defaults to
139 * {@value ReflectorConstants#SPECIFICATION_TRAVERSER_CLASS_NAME_DEFAULT}.
140 */
141 protected SpecificationTraverser createSpecificationTraverser(final IsisConfiguration configuration) {
142 final String specificationTraverserClassName = configuration.getString(ReflectorConstants.SPECIFICATION_TRAVERSER_CLASS_NAME, ReflectorConstants.SPECIFICATION_TRAVERSER_CLASS_NAME_DEFAULT);
143 final SpecificationTraverser specificationTraverser = InstanceUtil.createInstance(specificationTraverserClassName, SpecificationTraverser.class);
144 return specificationTraverser;
145 }
146
147 /**
148 * Hook method to allow subclasses to specify a different implementation of
149 * {@link MemberLayoutArranger}.
150 *
151 * <p>
152 * By default, looks up implementation from provided
153 * {@link IsisConfiguration} using
154 * {@link ReflectorConstants#MEMBER_LAYOUT_ARRANGER_CLASS_NAME}. If not
155 * specified, then defaults to
156 * {@value ReflectorConstants#MEMBER_LAYOUT_ARRANGER_CLASS_NAME_DEFAULT}.
157 */
158 protected MemberLayoutArranger createMemberLayoutArranger(final IsisConfiguration configuration) {
159 final String memberLayoutArrangerClassName = configuration.getString(ReflectorConstants.MEMBER_LAYOUT_ARRANGER_CLASS_NAME, ReflectorConstants.MEMBER_LAYOUT_ARRANGER_CLASS_NAME_DEFAULT);
160 final MemberLayoutArranger memberLayoutArranger = InstanceUtil.createInstance(memberLayoutArrangerClassName, MemberLayoutArranger.class);
161 return memberLayoutArranger;
162 }
163
164 /**
165 * Hook method to allow subclasses to specify a different implementations
166 * (that is, sets of {@link ProgrammingModel} .
167 *
168 * <p>
169 * By default, looks up implementation from provided
170 * {@link IsisConfiguration} using
171 * {@link ReflectorConstants#PROGRAMMING_MODEL_FACETS_CLASS_NAME}. If not
172 * specified, then defaults to
173 * {@value ReflectorConstants#PROGRAMMING_MODEL_FACETS_CLASS_NAME_DEFAULT}.
174 *
175 * <p>
176 * The list of facets can be adjusted using
177 * {@link ReflectorConstants#FACET_FACTORY_INCLUDE_CLASS_NAME_LIST} to
178 * specify additional {@link FacetFactory factories} to include, and
179 * {@link ReflectorConstants#FACET_FACTORY_EXCLUDE_CLASS_NAME_LIST} to
180 * exclude.
181 */
182 protected ProgrammingModel createProgrammingModelFacets(final IsisConfiguration configuration) {
183 final ProgrammingModel programmingModel = lookupAndCreateProgrammingModelFacets(configuration);
184 includeFacetFactories(configuration, programmingModel);
185 excludeFacetFactories(configuration, programmingModel);
186 return programmingModel;
187 }
188
189 private ProgrammingModel lookupAndCreateProgrammingModelFacets(final IsisConfiguration configuration) {
190 final String progModelFacetsClassName = configuration.getString(ReflectorConstants.PROGRAMMING_MODEL_FACETS_CLASS_NAME, ReflectorConstants.PROGRAMMING_MODEL_FACETS_CLASS_NAME_DEFAULT);
191 final ProgrammingModel programmingModel = InstanceUtil.createInstance(progModelFacetsClassName, ProgrammingModel.class);
192 return programmingModel;
193 }
194
195 /**
196 * Factored out of {@link #createProgrammingModelFacets(IsisConfiguration)}
197 * so that subclasses that choose to override can still support
198 * customization of their {@link ProgrammingModel} in a similar way.
199 */
200 protected void includeFacetFactories(final IsisConfiguration configuration, final ProgrammingModel programmingModel) {
201 final String[] facetFactoriesIncludeClassNames = configuration.getList(ReflectorConstants.FACET_FACTORY_INCLUDE_CLASS_NAME_LIST);
202 if (facetFactoriesIncludeClassNames != null) {
203 for (final String facetFactoryClassName : facetFactoriesIncludeClassNames) {
204 final Class<? extends FacetFactory> facetFactory = InstanceUtil.loadClass(facetFactoryClassName, FacetFactory.class);
205 programmingModel.addFactory(facetFactory);
206 }
207 }
208 }
209
210 /**
211 * Factored out of {@link #createProgrammingModelFacets(IsisConfiguration)}
212 * so that subclasses that choose to override can still support
213 * customization of their {@link ProgrammingModel} in a similar way.
214 */
215 protected void excludeFacetFactories(final IsisConfiguration configuration, final ProgrammingModel programmingModel) {
216 final String[] facetFactoriesExcludeClassNames = configuration.getList(ReflectorConstants.FACET_FACTORY_EXCLUDE_CLASS_NAME_LIST);
217 for (final String facetFactoryClassName : facetFactoriesExcludeClassNames) {
218 final Class<? extends FacetFactory> facetFactory = InstanceUtil.loadClass(facetFactoryClassName, FacetFactory.class);
219 programmingModel.removeFactory(facetFactory);
220 }
221 }
222
223 /**
224 * Hook method to allow subclasses to specify a different sets of
225 * {@link FacetDecorator}s.
226 *
227 * <p>
228 * By default, returns the {@link FacetDecorator}s that are specified in the
229 * {@link IsisConfiguration} (using
230 * {@link ReflectorConstants#FACET_DECORATOR_CLASS_NAMES}) along with any
231 * {@link FacetDecorator}s explicitly registered using
232 * {@link #addFacetDecoratorInstaller(FacetDecoratorInstaller)}. created
233 * using the {@link FacetDecoratorInstaller}s.
234 */
235 protected Set<FacetDecorator> createFacetDecorators(final IsisConfiguration configuration) {
236 addFacetDecoratorInstallers(configuration);
237 return createFacetDecorators(decoratorInstallers);
238 }
239
240 private void addFacetDecoratorInstallers(final IsisConfiguration configuration) {
241 final String[] decoratorNames = configuration.getList(ReflectorConstants.FACET_DECORATOR_CLASS_NAMES);
242 for (final String decoratorName : decoratorNames) {
243 if (LOG.isInfoEnabled()) {
244 LOG.info("adding reflector facet decorator from configuration " + decoratorName);
245 }
246 addFacetDecoratorInstaller(lookupFacetDecorator(decoratorName));
247 }
248 }
249
250 private FacetDecoratorInstaller lookupFacetDecorator(final String decoratorClassName) {
251 return installerLookup.getInstaller(FacetDecoratorInstaller.class, decoratorClassName);
252 }
253
254 private Set<FacetDecorator> createFacetDecorators(final Set<FacetDecoratorInstaller> decoratorInstallers) {
255 final LinkedHashSet<FacetDecorator> decorators = new LinkedHashSet<FacetDecorator>();
256 if (decoratorInstallers.size() == 0) {
257 if (LOG.isInfoEnabled()) {
258 LOG.info("No facet decorators installers added");
259 }
260 }
261 for (final FacetDecoratorInstaller installer : decoratorInstallers) {
262 decorators.addAll(installer.createDecorators());
263 }
264 return Collections.unmodifiableSet(decorators);
265 }
266
267 /**
268 * Hook method to allow subclasses to specify a different implementation of
269 * {@link MetaModelValidator}.
270 *
271 * <p>
272 * By default, looks up implementation from provided
273 * {@link IsisConfiguration} using
274 * {@link ReflectorConstants#META_MODEL_VALIDATOR_CLASS_NAME}. If not
275 * specified, then defaults to
276 * {@value ReflectorConstants#META_MODEL_VALIDATOR_CLASS_NAME_DEFAULT}.
277 */
278 protected MetaModelValidator createMetaModelValidator(final IsisConfiguration configuration) {
279 final String metaModelValidatorClassName = configuration.getString(ReflectorConstants.META_MODEL_VALIDATOR_CLASS_NAME, ReflectorConstants.META_MODEL_VALIDATOR_CLASS_NAME_DEFAULT);
280 final MetaModelValidator metaModelValidator = InstanceUtil.createInstance(metaModelValidatorClassName, MetaModelValidator.class);
281 return metaModelValidator;
282 }
283
284 /**
285 * Creates the {@link CollectionTypeRegistry}, hardcoded to be the
286 * {@link CollectionTypeRegistryDefault}.
287 *
288 * <p>
289 * Note: the intention is to remove this interface and instead to use a
290 * mechanism similar to the <tt>@Value</tt> annotation to specify which
291 * types represent collections. For now, have factored out this method
292 * similar to be similar to the creation methods of other subcomponents such
293 * as the {@link #createClassSubstitutor(IsisConfiguration)
294 * ClassSubstitutor}. Note however that this method is <tt>final</tt> so
295 * that it cannot be overridden.
296 */
297 protected final CollectionTypeRegistry createCollectionTypeRegistry(final IsisConfiguration configuration) {
298 return new CollectionTypeRegistryDefault();
299 }
300
301 /**
302 * Hook method to allow for other implementations (still based on
303 * {@link ObjectReflectorDefault}).
304 */
305 protected ObjectReflectorDefault doCreateReflector(final IsisConfiguration configuration, final ClassSubstitutor classSubstitutor, final CollectionTypeRegistry collectionTypeRegistry, final SpecificationTraverser specificationTraverser, final MemberLayoutArranger memberLayoutArranger,
306 final ProgrammingModel programmingModel, final Set<FacetDecorator> facetDecorators, final MetaModelValidator metaModelValidator) {
307 return new ObjectReflectorDefault(configuration, classSubstitutor, collectionTypeRegistry, specificationTraverser, memberLayoutArranger, programmingModel, facetDecorators, metaModelValidator);
308 }
309
310 // /////////////////////////////////////////////////////
311 // Optionally Injected: InstallerLookup
312 // /////////////////////////////////////////////////////
313
314 /**
315 * Injected by virtue of being {@link InstallerLookupAware}.
316 */
317 @Override
318 public void setInstallerLookup(final InstallerLookup installerLookup) {
319 this.installerLookup = installerLookup;
320 }
321
322 // /////////////////////////////////////////////////////
323 // Optionally Injected: DecoratorInstallers
324 // /////////////////////////////////////////////////////
325
326 /**
327 * Adds in {@link FacetDecoratorInstaller}; if <tt>null</tt> or if already
328 * added then request will be silently ignored.
329 */
330 @Override
331 public void addFacetDecoratorInstaller(final FacetDecoratorInstaller decoratorInstaller) {
332 if (decoratorInstaller == null) {
333 return;
334 }
335 decoratorInstallers.add(decoratorInstaller);
336 }
337
338 // /////////////////////////////////////////////////////
339 // Guice
340 // /////////////////////////////////////////////////////
341
342 @Override
343 public List<Class<?>> getTypes() {
344 return listOf(ObjectReflector.class);
345 }
346 }