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.platform.types.localconfiguration.UITypesConfiguration;
042import org.nuxeo.runtime.api.Framework;
043import org.nuxeo.runtime.model.ComponentContext;
044import org.nuxeo.runtime.model.ComponentInstance;
045import org.nuxeo.runtime.model.ComponentName;
046import org.nuxeo.runtime.model.DefaultComponent;
047
048public class TypeService extends DefaultComponent implements TypeManager {
049
050    public static final ComponentName ID = new ComponentName("org.nuxeo.ecm.platform.types.TypeService");
051
052    private static final Log log = LogFactory.getLog(TypeService.class);
053
054    public static String DEFAULT_CATEGORY = "misc";
055
056    public static final String HIDDEN_IN_CREATION = "create";
057
058    private TypeRegistry typeRegistry;
059
060    @Override
061    public void activate(ComponentContext context) {
062        typeRegistry = new TypeRegistry();
063    }
064
065    @Override
066    public void deactivate(ComponentContext context) {
067        typeRegistry = null;
068    }
069
070    @Override
071    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
072        if (extensionPoint.equals("types")) {
073            typeRegistry.addContribution((Type) contribution);
074        }
075    }
076
077    @Override
078    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
079        if (extensionPoint.equals("types")) {
080            typeRegistry.removeContribution((Type) contribution);
081        }
082    }
083
084    public TypeRegistry getTypeRegistry() {
085        return typeRegistry;
086    }
087
088    // Service implementation for TypeManager interface
089
090    @Override
091    public String[] getSuperTypes(String typeName) {
092        SchemaManager schemaMgr = Framework.getService(SchemaManager.class);
093        DocumentType type = schemaMgr.getDocumentType(typeName);
094        if (type == null) {
095            return null;
096        }
097        type = (DocumentType) type.getSuperType();
098        List<String> superTypes = new ArrayList<String>();
099        while (type != null) {
100            superTypes.add(type.getName());
101            type = (DocumentType) type.getSuperType();
102        }
103        return superTypes.toArray(new String[superTypes.size()]);
104    }
105
106    @Override
107    public Type getType(String typeName) {
108        return typeRegistry.getType(typeName);
109    }
110
111    @Override
112    public boolean hasType(String typeName) {
113        return typeRegistry.hasType(typeName);
114    }
115
116    @Override
117    public Collection<Type> getTypes() {
118        Collection<Type> types = new ArrayList<Type>();
119        types.addAll(typeRegistry.getTypes());
120        return types;
121    }
122
123    @Override
124    public Collection<Type> getAllowedSubTypes(String typeName) {
125        return getAllowedSubTypes(typeName, null);
126    }
127
128    @Override
129    public Collection<Type> getAllowedSubTypes(String typeName, DocumentModel currentDoc) {
130        Collection<Type> allowed = new ArrayList<Type>();
131        Type type = getType(typeName);
132        if (type != null) {
133            Map<String, SubType> allowedSubTypes = type.getAllowedSubTypes();
134            if (currentDoc != null) {
135                allowedSubTypes = filterSubTypesFromConfiguration(allowedSubTypes, currentDoc);
136            }
137            for (String subTypeName : allowedSubTypes.keySet()) {
138                Type subType = getType(subTypeName);
139                if (subType != null) {
140                    allowed.add(subType);
141                }
142            }
143        }
144        return allowed;
145    }
146
147    @Override
148    public Collection<Type> findAllAllowedSubTypesFrom(String typeName) {
149        return findAllAllowedSubTypesFrom(typeName, null, null);
150    }
151
152    @Override
153    public Collection<Type> findAllAllowedSubTypesFrom(String typeName, DocumentModel currentDoc) {
154        return findAllAllowedSubTypesFrom(typeName, currentDoc, null);
155    }
156
157    protected Collection<Type> findAllAllowedSubTypesFrom(String typeName, DocumentModel currentDoc,
158            List<String> alreadyProcessedTypes) {
159        if (alreadyProcessedTypes == null) {
160            alreadyProcessedTypes = new ArrayList<String>();
161        }
162        Set<Type> allAllowedSubTypes = new HashSet<Type>();
163
164        Collection<Type> allowedSubTypes = getAllowedSubTypes(typeName, currentDoc);
165        allAllowedSubTypes.addAll(allowedSubTypes);
166        alreadyProcessedTypes.add(typeName);
167        for (Type subType : allowedSubTypes) {
168            if (!alreadyProcessedTypes.contains(subType.getId())) {
169                allAllowedSubTypes.addAll(findAllAllowedSubTypesFrom(subType.getId(), currentDoc, alreadyProcessedTypes));
170            }
171        }
172
173        return allAllowedSubTypes;
174    }
175
176    protected UITypesConfiguration getConfiguration(DocumentModel currentDoc) {
177        LocalConfigurationService localConfigurationService = Framework.getService(LocalConfigurationService.class);
178        return localConfigurationService.getConfiguration(UITypesConfiguration.class, UI_TYPES_CONFIGURATION_FACET,
179                currentDoc);
180    }
181
182    @Override
183    public Map<String, List<Type>> getTypeMapForDocumentType(String typeName, DocumentModel currentDoc) {
184        Type type = getType(typeName);
185        if (type != null) {
186            Map<String, List<Type>> docTypesMap = new HashMap<String, List<Type>>();
187            Map<String, SubType> allowedSubTypes = type.getAllowedSubTypes();
188            allowedSubTypes = filterSubTypesFromConfiguration(allowedSubTypes, currentDoc);
189            for (Map.Entry<String, SubType> entry : allowedSubTypes.entrySet()) {
190                if (canCreate(entry.getValue())) {
191                    Type subType = getType(entry.getKey());
192                    if (subType != null) {
193                        String key = subType.getCategory();
194                        if (key == null) {
195                            key = DEFAULT_CATEGORY;
196                        }
197                        if (!docTypesMap.containsKey(key)) {
198                            docTypesMap.put(key, new ArrayList<Type>());
199                        }
200                        docTypesMap.get(key).add(subType);
201                    }
202                }
203            }
204            return docTypesMap;
205        }
206        return new HashMap<String, List<Type>>();
207    }
208
209    @Override
210    public boolean canCreate(String typeName, String containerTypeName) {
211        Type containerType = getType(containerTypeName);
212        Map<String, SubType> allowedSubTypes = containerType.getAllowedSubTypes();
213        return canCreate(typeName, allowedSubTypes);
214    }
215
216    @Override
217    public boolean canCreate(String typeName, String containerTypeName, DocumentModel currentDoc) {
218        Map<String, SubType> allowedSubTypes = getFilteredAllowedSubTypes(containerTypeName, currentDoc);
219        return canCreate(typeName, allowedSubTypes);
220    }
221
222    protected Map<String, SubType> getFilteredAllowedSubTypes(String containerTypeName, DocumentModel currentDoc) {
223        Type containerType = getType(containerTypeName);
224        if (containerType == null) {
225            return Collections.emptyMap();
226        }
227        Map<String, SubType> allowedSubTypes = containerType.getAllowedSubTypes();
228        return filterSubTypesFromConfiguration(allowedSubTypes, currentDoc);
229    }
230
231    protected boolean canCreate(String typeName, Map<String, SubType> allowedSubTypes) {
232        if (!isAllowedSubType(typeName, allowedSubTypes)) {
233            return false;
234        }
235
236        SubType subType = allowedSubTypes.get(typeName);
237        return canCreate(subType);
238    }
239
240    protected boolean canCreate(SubType subType) {
241        List<String> hidden = subType.getHidden();
242        return !(hidden != null && hidden.contains(HIDDEN_IN_CREATION));
243    }
244
245    @Override
246    public boolean isAllowedSubType(String typeName, String containerTypeName) {
247        Type containerType = getType(containerTypeName);
248        if (containerType == null) {
249            return false;
250        }
251        Map<String, SubType> allowedSubTypes = containerType.getAllowedSubTypes();
252        return isAllowedSubType(typeName, allowedSubTypes);
253    }
254
255    protected boolean isAllowedSubType(String typeName, Map<String, SubType> allowedSubTypes) {
256        for (String subTypeName : allowedSubTypes.keySet()) {
257            if (subTypeName.equals(typeName)) {
258                return true;
259            }
260        }
261        return false;
262    }
263
264    @Override
265    public boolean isAllowedSubType(String typeName, String containerTypeName, DocumentModel currentDoc) {
266        Map<String, SubType> allowedSubTypes = getFilteredAllowedSubTypes(containerTypeName, currentDoc);
267        return isAllowedSubType(typeName, allowedSubTypes);
268    }
269
270    protected Map<String, SubType> filterSubTypesFromConfiguration(Map<String, SubType> allowedSubTypes,
271            DocumentModel currentDoc) {
272        UITypesConfiguration uiTypesConfiguration = getConfiguration(currentDoc);
273        if (uiTypesConfiguration != null) {
274            allowedSubTypes = uiTypesConfiguration.filterSubTypes(allowedSubTypes);
275        }
276        return allowedSubTypes;
277    }
278
279}