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