View Javadoc
1   /**
2    * This file Copyright (c) 2013-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.form.fieldtype.registry;
35  
36  import info.magnolia.config.registry.AbstractRegistry;
37  import info.magnolia.config.registry.DefinitionMetadataBuilder;
38  import info.magnolia.config.registry.DefinitionProvider;
39  import info.magnolia.config.registry.DefinitionProviderBuilder;
40  import info.magnolia.config.registry.DefinitionType;
41  import info.magnolia.module.ModuleRegistry;
42  import info.magnolia.objectfactory.Components;
43  import info.magnolia.registry.RegistrationException;
44  import info.magnolia.ui.form.field.definition.ConfiguredFieldDefinition;
45  import info.magnolia.ui.form.field.definition.FieldDefinition;
46  import info.magnolia.ui.form.fieldtype.definition.FieldTypeDefinition;
47  
48  import java.util.Optional;
49  import java.util.concurrent.ExecutionException;
50  import java.util.concurrent.TimeUnit;
51  
52  import javax.inject.Inject;
53  import javax.inject.Singleton;
54  
55  import com.google.common.cache.CacheBuilder;
56  import com.google.common.cache.CacheLoader;
57  import com.google.common.cache.LoadingCache;
58  
59  import net.sf.cglib.proxy.Enhancer;
60  
61  /**
62   * FieldTypeDefinitionRegistry.
63   */
64  @Singleton
65  public class FieldTypeDefinitionRegistry extends AbstractRegistry<FieldTypeDefinition> {
66  
67      private static final DefinitionProvider<FieldTypeDefinition> NULL_DEFINITION_PROVIDER = DefinitionProviderBuilder.<FieldTypeDefinition>newBuilder().build();
68  
69      private final LoadingCache<Class, DefinitionProvider<FieldTypeDefinition>> definitionProviderCache =
70              CacheBuilder.newBuilder()
71                      .maximumSize(100)
72                      .refreshAfterWrite(1000, TimeUnit.MILLISECONDS)
73                      .initialCapacity(100)
74                      .build(new CacheLoader<Class, DefinitionProvider<FieldTypeDefinition>>() {
75                          @Override
76                          public DefinitionProvider<FieldTypeDefinition> load(Class key) throws Exception {
77                              return resolveMostActualDefinitionProvider(key);
78                          }
79                      });
80  
81      @Inject
82      public FieldTypeDefinitionRegistry(ModuleRegistry moduleRegistry) {
83          super(moduleRegistry);
84      }
85  
86      /**
87       * @deprecated since 5.4.6 - use {@link #FieldTypeDefinitionRegistry(ModuleRegistry)} instead.
88       */
89      @Deprecated
90      public FieldTypeDefinitionRegistry() {
91          this(Components.getComponent(ModuleRegistry.class));
92      }
93  
94      public Optional<DefinitionProvider<FieldTypeDefinition>> getByDefinitionType(Class<? extends FieldDefinition> definitionClass) {
95          try {
96              return Optional.ofNullable(definitionProviderCache.get(definitionClass, () -> resolveMostActualDefinitionProvider(definitionClass)));
97          } catch (ExecutionException e) {
98              return Optional.empty();
99          }
100     }
101 
102     public DefinitionProvider<FieldTypeDefinition> resolveMostActualDefinitionProvider(Class<? extends FieldDefinition> definitionClass) throws RegistrationException {
103         // Should the provided class be proxied by either i18n-mechanism or some other
104         // functionality - un-wrap the original type.
105         while (Enhancer.isEnhanced(definitionClass)) {
106             definitionClass = (Class<? extends FieldDefinition>) definitionClass.getSuperclass();
107         }
108 
109         if (definitionClass.equals(ConfiguredFieldDefinition.class)) {
110             // FIXME MGNLUI-829 Working around side effect of extend=override, can't do anything with ConfiguredFieldDefinition.
111             throw new RegistrationException("");
112         }
113 
114         DefinitionProvider<FieldTypeDefinition> deprecatedFieldTypeDefinition = null;
115         DefinitionProvider<FieldTypeDefinition> nonDeprecatedFieldTypeDefinition = null;
116         for (DefinitionProvider<FieldTypeDefinition> provider : getRegistryMap().values()) {
117             provider = getDecoratedDefinitionProvider(provider);
118             final FieldTypeDefinition fieldTypeDefinition = provider.get();
119             if (definitionClass.equals(fieldTypeDefinition.getDefinitionClass())) {
120                 // No matter what we prioritize decorated definitions first
121                 if (provider.getDecorators().size() > 0) {
122                     return provider;
123                 }
124 
125                 // We store the deprecated and non-deprecated providers
126                 if (!provider.getMetadata().getDeprecation().isPresent()) {
127                     nonDeprecatedFieldTypeDefinition = provider;
128                 } else {
129                     deprecatedFieldTypeDefinition = provider;
130                 }
131             }
132         }
133 
134         // We couldn't find any decorated ones, therefore we return non deprecated definition.
135         if (nonDeprecatedFieldTypeDefinition != null) {
136             return nonDeprecatedFieldTypeDefinition;
137         }
138 
139         // At this stage we don't have any non-deprecated definitions nor decorated ones,
140         // therefore we return deprecated one if it exists
141         if (deprecatedFieldTypeDefinition != null) {
142             return deprecatedFieldTypeDefinition;
143         }
144 
145         throw new RegistrationException("Could not find fieldType for definition " + definitionClass.getName());
146     }
147 
148     public FieldTypeDefinition getByDefinition(Class<? extends FieldDefinition> definitionClass) throws RegistrationException {
149         return getByDefinitionType(definitionClass)
150                 .filter(DefinitionProvider::isValid)
151                 .map(DefinitionProvider::get)
152                 .orElseThrow(() -> new RegistrationException("Field type definition not found or invalid for field definition type: " + definitionClass.getName()));
153     }
154 
155     /**
156      * @deprecated since 5.4 - use the {@link #getProvider(String)} method instead and fetch definition from its result.
157      */
158     @Deprecated
159     public FieldTypeDefinition get(String id) throws RegistrationException {
160         final FieldTypeDefinition fieldTypeDefinition;
161         try {
162             fieldTypeDefinition = getProvider(id).get();
163         } catch (NoSuchDefinitionException | InvalidDefinitionException e) {
164             throw new RegistrationException(e.getMessage(), e);
165         }
166         return fieldTypeDefinition;
167     }
168 
169     @Override
170     public DefinitionType type() {
171         return DefinitionTypes.FIELD_TYPE;
172     }
173 
174     @Override
175     public DefinitionMetadataBuilder newMetadataBuilder() {
176         return DefinitionMetadataBuilder.usingNameAsId();
177     }
178 }