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    }