001/*
002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.platform.types;
023
024import static org.nuxeo.ecm.platform.types.localconfiguration.UITypesConfigurationConstants.UI_TYPES_CONFIGURATION_FACET;
025
026import java.util.ArrayList;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.HashMap;
030import java.util.HashSet;
031import java.util.List;
032import java.util.Map;
033import java.util.Set;
034
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037import org.nuxeo.ecm.core.api.DocumentModel;
038import org.nuxeo.ecm.core.api.localconfiguration.LocalConfigurationService;
039import org.nuxeo.ecm.core.schema.DocumentType;
040import org.nuxeo.ecm.core.schema.SchemaManager;
041import org.nuxeo.ecm.core.schema.SchemaManagerImpl;
042import org.nuxeo.ecm.platform.types.localconfiguration.UITypesConfiguration;
043import org.nuxeo.runtime.api.Framework;
044import org.nuxeo.runtime.model.ComponentContext;
045import org.nuxeo.runtime.model.ComponentInstance;
046import org.nuxeo.runtime.model.ComponentName;
047import org.nuxeo.runtime.model.DefaultComponent;
048
049public class TypeService extends DefaultComponent implements TypeManager {
050
051    public static final ComponentName ID = new ComponentName("org.nuxeo.ecm.platform.types.TypeService");
052
053    private static final Log log = LogFactory.getLog(TypeService.class);
054
055    public static String DEFAULT_CATEGORY = "misc";
056
057    public static final String HIDDEN_IN_CREATION = "create";
058
059    private TypeRegistry typeRegistry;
060
061    private Runnable recomputeCallback;
062
063    @Override
064    public void activate(ComponentContext context) {
065        typeRegistry = new TypeRegistry();
066        recomputeCallback = typeRegistry::recomputeTypes;
067        SchemaManagerImpl schemaManager = (SchemaManagerImpl) Framework.getService(SchemaManager.class);
068        schemaManager.registerRecomputeCallback(recomputeCallback);
069    }
070
071    @Override
072    public void deactivate(ComponentContext context) {
073        SchemaManagerImpl schemaManager = (SchemaManagerImpl)Framework.getService(SchemaManager.class);
074        schemaManager.unregisterRecomputeCallback(recomputeCallback);
075        typeRegistry = null;
076    }
077
078    @Override
079    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
080        if (extensionPoint.equals("types")) {
081            typeRegistry.addContribution((Type) contribution);
082        }
083    }
084
085    @Override
086    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
087        if (extensionPoint.equals("types")) {
088            typeRegistry.removeContribution((Type) contribution);
089        }
090    }
091
092    public TypeRegistry getTypeRegistry() {
093        return typeRegistry;
094    }
095
096    // Service implementation for TypeManager interface
097
098    @Override
099    public String[] getSuperTypes(String typeName) {
100        SchemaManager schemaMgr = Framework.getService(SchemaManager.class);
101        DocumentType type = schemaMgr.getDocumentType(typeName);
102        if (type == null) {
103            return null;
104        }
105        type = (DocumentType) type.getSuperType();
106        List<String> superTypes = new ArrayList<String>();
107        while (type != null) {
108            superTypes.add(type.getName());
109            type = (DocumentType) type.getSuperType();
110        }
111        return superTypes.toArray(new String[superTypes.size()]);
112    }
113
114    @Override
115    public Type getType(String typeName) {
116        return typeRegistry.getType(typeName);
117    }
118
119    @Override
120    public boolean hasType(String typeName) {
121        return typeRegistry.hasType(typeName);
122    }
123
124    @Override
125    public Collection<Type> getTypes() {
126        Collection<Type> types = new ArrayList<Type>();
127        types.addAll(typeRegistry.getTypes());
128        return types;
129    }
130
131    @Override
132    public Collection<Type> getAllowedSubTypes(String typeName) {
133        return getAllowedSubTypes(typeName, null);
134    }
135
136    @Override
137    public Collection<Type> getAllowedSubTypes(String typeName, DocumentModel currentDoc) {
138        Collection<Type> allowed = new ArrayList<Type>();
139        Type type = getType(typeName);
140        if (type != null) {
141            Map<String, SubType> allowedSubTypes = type.getAllowedSubTypes();
142            if (currentDoc != null) {
143                allowedSubTypes = filterSubTypesFromConfiguration(allowedSubTypes, currentDoc);
144            }
145            for (String subTypeName : allowedSubTypes.keySet()) {
146                Type subType = getType(subTypeName);
147                if (subType != null) {
148                    allowed.add(subType);
149                }
150            }
151        }
152        return allowed;
153    }
154
155    @Override
156    public Collection<Type> findAllAllowedSubTypesFrom(String typeName) {
157        return findAllAllowedSubTypesFrom(typeName, null, null);
158    }
159
160    @Override
161    public Collection<Type> findAllAllowedSubTypesFrom(String typeName, DocumentModel currentDoc) {
162        return findAllAllowedSubTypesFrom(typeName, currentDoc, null);
163    }
164
165    protected Collection<Type> findAllAllowedSubTypesFrom(String typeName, DocumentModel currentDoc,
166            List<String> alreadyProcessedTypes) {
167        if (alreadyProcessedTypes == null) {
168            alreadyProcessedTypes = new ArrayList<String>();
169        }
170        Set<Type> allAllowedSubTypes = new HashSet<Type>();
171
172        Collection<Type> allowedSubTypes = getAllowedSubTypes(typeName, currentDoc);
173        allAllowedSubTypes.addAll(allowedSubTypes);
174        alreadyProcessedTypes.add(typeName);
175        for (Type subType : allowedSubTypes) {
176            if (!alreadyProcessedTypes.contains(subType.getId())) {
177                allAllowedSubTypes.addAll(findAllAllowedSubTypesFrom(subType.getId(), currentDoc, alreadyProcessedTypes));
178            }
179        }
180
181        return allAllowedSubTypes;
182    }
183
184    protected UITypesConfiguration getConfiguration(DocumentModel currentDoc) {
185        LocalConfigurationService localConfigurationService = Framework.getService(LocalConfigurationService.class);
186        return localConfigurationService.getConfiguration(UITypesConfiguration.class, UI_TYPES_CONFIGURATION_FACET,
187                currentDoc);
188    }
189
190    @Override
191    public Map<String, List<Type>> getTypeMapForDocumentType(String typeName, DocumentModel currentDoc) {
192        Type type = getType(typeName);
193        if (type != null) {
194            Map<String, List<Type>> docTypesMap = new HashMap<String, List<Type>>();
195            Map<String, SubType> allowedSubTypes = type.getAllowedSubTypes();
196            allowedSubTypes = filterSubTypesFromConfiguration(allowedSubTypes, currentDoc);
197            for (Map.Entry<String, SubType> entry : allowedSubTypes.entrySet()) {
198                if (canCreate(entry.getValue())) {
199                    Type subType = getType(entry.getKey());
200                    if (subType != null) {
201                        String key = subType.getCategory();
202                        if (key == null) {
203                            key = DEFAULT_CATEGORY;
204                        }
205                        if (!docTypesMap.containsKey(key)) {
206                            docTypesMap.put(key, new ArrayList<Type>());
207                        }
208                        docTypesMap.get(key).add(subType);
209                    }
210                }
211            }
212            return docTypesMap;
213        }
214        return new HashMap<String, List<Type>>();
215    }
216
217    @Override
218    public boolean canCreate(String typeName, String containerTypeName) {
219        Type containerType = getType(containerTypeName);
220        Map<String, SubType> allowedSubTypes = containerType.getAllowedSubTypes();
221        return canCreate(typeName, allowedSubTypes);
222    }
223
224    @Override
225    public boolean canCreate(String typeName, String containerTypeName, DocumentModel currentDoc) {
226        Map<String, SubType> allowedSubTypes = getFilteredAllowedSubTypes(containerTypeName, currentDoc);
227        return canCreate(typeName, allowedSubTypes);
228    }
229
230    protected Map<String, SubType> getFilteredAllowedSubTypes(String containerTypeName, DocumentModel currentDoc) {
231        Type containerType = getType(containerTypeName);
232        if (containerType == null) {
233            return Collections.emptyMap();
234        }
235        Map<String, SubType> allowedSubTypes = containerType.getAllowedSubTypes();
236        return filterSubTypesFromConfiguration(allowedSubTypes, currentDoc);
237    }
238
239    protected boolean canCreate(String typeName, Map<String, SubType> allowedSubTypes) {
240        if (!isAllowedSubType(typeName, allowedSubTypes)) {
241            return false;
242        }
243
244        SubType subType = allowedSubTypes.get(typeName);
245        return canCreate(subType);
246    }
247
248    protected boolean canCreate(SubType subType) {
249        List<String> hidden = subType.getHidden();
250        return !(hidden != null && hidden.contains(HIDDEN_IN_CREATION));
251    }
252
253    @Override
254    public boolean isAllowedSubType(String typeName, String containerTypeName) {
255        Type containerType = getType(containerTypeName);
256        if (containerType == null) {
257            return false;
258        }
259        Map<String, SubType> allowedSubTypes = containerType.getAllowedSubTypes();
260        return isAllowedSubType(typeName, allowedSubTypes);
261    }
262
263    protected boolean isAllowedSubType(String typeName, Map<String, SubType> allowedSubTypes) {
264        for (String subTypeName : allowedSubTypes.keySet()) {
265            if (subTypeName.equals(typeName)) {
266                return true;
267            }
268        }
269        return false;
270    }
271
272    @Override
273    public boolean isAllowedSubType(String typeName, String containerTypeName, DocumentModel currentDoc) {
274        Map<String, SubType> allowedSubTypes = getFilteredAllowedSubTypes(containerTypeName, currentDoc);
275        return isAllowedSubType(typeName, allowedSubTypes);
276    }
277
278    protected Map<String, SubType> filterSubTypesFromConfiguration(Map<String, SubType> allowedSubTypes,
279            DocumentModel currentDoc) {
280        UITypesConfiguration uiTypesConfiguration = getConfiguration(currentDoc);
281        if (uiTypesConfiguration != null) {
282            allowedSubTypes = uiTypesConfiguration.filterSubTypes(allowedSubTypes);
283        }
284        return allowedSubTypes;
285    }
286
287}