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 }