View Javadoc

1   /*
2    * Entreri, an entity-component framework in Java
3    *
4    * Copyright (c) 2013, Michael Ludwig
5    * All rights reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without modification,
8    * are permitted provided that the following conditions are met:
9    *
10   *     Redistributions of source code must retain the above copyright notice,
11   *         this list of conditions and the following disclaimer.
12   *     Redistributions in binary form must reproduce the above copyright notice,
13   *         this list of conditions and the following disclaimer in the
14   *         documentation and/or other materials provided with the distribution.
15   *
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
20   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   */
27  package com.lhkbob.entreri.impl;
28  
29  import com.lhkbob.entreri.Component;
30  
31  import java.lang.reflect.Constructor;
32  import java.lang.reflect.InvocationTargetException;
33  import java.lang.reflect.ParameterizedType;
34  import java.lang.reflect.Type;
35  
36  /**
37   * CompiledFactoryProvider searches the classpath for existing class definitions of the
38   * component types. This is suitable for generating component implementations as part of a
39   * project build in conjunction with the {@link ComponentImplementationProcessor}.
40   *
41   * @author Michael Ludwig
42   */
43  class CompiledFactoryProvider extends ComponentFactoryProvider {
44      @Override
45      public <T extends Component> Factory<T> getFactory(Class<T> componentType) {
46          try {
47              return new CompiledFactory<>(componentType);
48          } catch (ClassNotFoundException cfe) {
49              // if class is not present we just return null to delegate to other providers
50              // (which could be Janino-based, but we're not using that right now)
51              return null;
52          }
53      }
54  
55      private static class CompiledFactory<T extends Component> implements Factory<T> {
56          final Class<? extends AbstractComponent<T>> implType;
57          final ComponentSpecification specification;
58  
59          final Constructor<? extends AbstractComponent<T>> ctor;
60  
61          @SuppressWarnings("unchecked")
62          public CompiledFactory(Class<T> type) throws ClassNotFoundException {
63              specification = ComponentSpecification.Factory.fromClass(type);
64              String implName = ComponentFactoryProvider
65                      .getImplementationClassName(specification, true);
66  
67              Class<?> loaded = Class.forName(implName);
68              // although the compiled classes should have been produced from the same
69              // generated source used by APT, we can't be certain because we're reading
70              // them from the classpath, so this factory has to validate certain elements
71              // of the component type
72              if (!loaded.getSuperclass().equals(AbstractComponent.class)) {
73                  throw new IllegalStateException(
74                          "Discovered impl. class does not extend AbstractComponent for " +
75                          type);
76              }
77              Type paramType = ((ParameterizedType) loaded.getGenericSuperclass())
78                      .getActualTypeArguments()[0];
79              if (!paramType.equals(type)) {
80                  throw new IllegalStateException(
81                          "Discovered impl. uses wrong type parameter for AbstractComponent, was " +
82                          paramType + " instead of " + type);
83              }
84              if (!type.isAssignableFrom(loaded)) {
85                  throw new IllegalStateException(
86                          "Discovered impl. does not implement the expected interface: " +
87                          type);
88              }
89  
90              // at this point it's a safe cast
91              implType = (Class<? extends AbstractComponent<T>>) loaded;
92  
93              try {
94                  ctor = implType.getConstructor(ComponentRepository.class);
95              } catch (NoSuchMethodException e) {
96                  throw new IllegalStateException(
97                          "Discovered impl. does not have mandated constructor for component: " +
98                          type);
99              }
100         }
101 
102 
103         @Override
104         public AbstractComponent<T> newInstance(ComponentRepository<T> forRepository) {
105             try {
106                 return ctor.newInstance(forRepository);
107             } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
108                 throw new RuntimeException(
109                         "Exception instantiating compiled component impl", e);
110             }
111         }
112 
113         @Override
114         public ComponentSpecification getSpecification() {
115             return specification;
116         }
117     }
118 }