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: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $ 020 */ 021 022package org.nuxeo.ecm.webapp.action; 023 024import static org.jboss.seam.ScopeType.CONVERSATION; 025import static org.jboss.seam.ScopeType.EVENT; 026 027import java.io.Serializable; 028import java.util.ArrayList; 029import java.util.HashMap; 030import java.util.List; 031import java.util.Map; 032import java.util.Map.Entry; 033import java.util.Set; 034 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037import org.jboss.seam.annotations.Factory; 038import org.jboss.seam.annotations.In; 039import org.jboss.seam.annotations.Install; 040import org.jboss.seam.annotations.Name; 041import org.jboss.seam.annotations.Observer; 042import org.jboss.seam.annotations.Scope; 043import org.jboss.seam.annotations.intercept.BypassInterceptors; 044import org.nuxeo.ecm.core.api.DocumentModel; 045import org.nuxeo.ecm.platform.types.Type; 046import org.nuxeo.ecm.platform.types.TypeManager; 047import org.nuxeo.ecm.platform.ui.web.api.NavigationContext; 048import org.nuxeo.ecm.webapp.helpers.EventNames; 049import org.nuxeo.ecm.webapp.seam.NuxeoSeamHotReloader; 050 051/** 052 * Document type service for document type creation. 053 * 054 * @author eionica@nuxeo.com 055 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 056 */ 057@Name("typesTool") 058@Scope(CONVERSATION) 059@Install(precedence = Install.FRAMEWORK) 060public class TypesTool implements Serializable { 061 062 private static final long serialVersionUID = -5037578301250616973L; 063 064 protected static final Log log = LogFactory.getLog(TypesTool.class); 065 066 private static final int COLUMN_SIZE = 4; 067 068 @In(create = true) 069 protected transient TypeManager typeManager; 070 071 @In(create = true) 072 protected NuxeoSeamHotReloader seamReload; 073 074 protected Map<String, List<List<Type>>> typesMap; 075 076 protected Long typesMapTimestamp; 077 078 protected Type selectedType; 079 080 @In(create = true) 081 protected transient NavigationContext navigationContext; 082 083 @Observer(value = { EventNames.CONTENT_ROOT_SELECTION_CHANGED, EventNames.DOCUMENT_SELECTION_CHANGED, 084 EventNames.DOMAIN_SELECTION_CHANGED, EventNames.LOCAL_CONFIGURATION_CHANGED }, create = false) 085 @BypassInterceptors 086 public void resetTypesList() { 087 typesMap = null; 088 typesMapTimestamp = null; 089 } 090 091 /** 092 * Retrieves the list of allowed sub types given a current type. 093 * <p> 094 * This is used at creation time. Current type is retrieved thanks to the document model hold and passed by the 095 * event. 096 */ 097 public void populateTypesList() { 098 boolean set = false; 099 DocumentModel model = getCurrentItem(); 100 if (model != null) { 101 typesMap = getOrganizedTypeMapForDocumentType(model.getType()); 102 set = true; 103 } 104 if (!set) { 105 // set an empty list 106 typesMap = new HashMap<String, List<List<Type>>>(); 107 } 108 typesMapTimestamp = typeManager.getLastModified(); 109 } 110 111 public Map<String, List<List<Type>>> getOrganizedTypeMapForDocumentType(String type) { 112 Map<String, List<Type>> docTypesMap = typeManager.getTypeMapForDocumentType(type, getConfigurationDocument()); 113 docTypesMap = filterTypeMap(docTypesMap); 114 return organizeType(docTypesMap); 115 } 116 117 /** 118 * Returns the Configuration document to be used as the local configuration of the {@code TypeManager}. 119 * <p> 120 * This method can be overridden by Subclasses to define a specific Configuration document. 121 * 122 * @since 5.4.2 123 */ 124 protected DocumentModel getConfigurationDocument() { 125 return navigationContext.getCurrentDocument(); 126 } 127 128 /** 129 * Method to be overridden by subclasses to filter the type Map. 130 * 131 * @since 5.4.2 132 */ 133 protected Map<String, List<Type>> filterTypeMap(Map<String, List<Type>> docTypeMap) { 134 return docTypeMap; 135 } 136 137 /** 138 * Split each @{code List} of {@code Type} in one or more new {@code List}, with maximum 4 {@code Type}s in each new 139 * {@code List} and returns the new computed {@code Map}. 140 */ 141 protected Map<String, List<List<Type>>> organizeType(Map<String, List<Type>> types) { 142 Map<String, List<List<Type>>> newTypesMap = new HashMap<String, List<List<Type>>>(); 143 Set<Entry<String, List<Type>>> typeEntrySet = types.entrySet(); 144 for (Entry<String, List<Type>> set : typeEntrySet) { 145 List<Type> typeList = set.getValue(); 146 List<List<Type>> newList = new ArrayList<List<Type>>(); 147 int index = 0; 148 newList.add(index, new ArrayList<Type>()); 149 for (Type type : typeList) { 150 List<Type> currentList = newList.get(index); 151 if (currentList == null) { 152 currentList = new ArrayList<Type>(); 153 newList.add(index, currentList); 154 } 155 currentList.add(type); 156 if (currentList.size() % COLUMN_SIZE == 0) { 157 index++; 158 newList.add(index, new ArrayList<Type>()); 159 } 160 } 161 newTypesMap.put(set.getKey(), newList); 162 } 163 return newTypesMap; 164 } 165 166 public Type getSelectedType() { 167 if (selectedType != null) { 168 log.debug("Returning selected type with id: " + selectedType.getId()); 169 } 170 return selectedType; 171 } 172 173 /** 174 * If the selected type is supposed to be automatically injected by Seam through @DataModelSelection callback (i.e. 175 * the user will select the type from a list), this method should be called with <code>null</code> parameter before. 176 */ 177 public void setSelectedType(Type type) { 178 if (typesMap == null) { 179 populateTypesList(); 180 } 181 selectedType = type; 182 } 183 184 @Factory(value = "typesMap", scope = EVENT) 185 public Map<String, List<List<Type>>> getTypesList() { 186 // XXX : should cache per currentDocument type 187 if (typesMap == null 188 || (seamReload.isDevModeSet() && seamReload.shouldResetCache(typeManager, typesMapTimestamp))) { 189 // cache the list of allowed subtypes 190 populateTypesList(); 191 } 192 selectedType = null; 193 return typesMap; 194 } 195 196 public void setTypesList(Map<String, List<List<Type>>> typesList) { 197 this.typesMap = typesList; 198 } 199 200 public Type getType(String typeName) { 201 return typeManager.getType(typeName); 202 } 203 204 public boolean hasType(String typeName) { 205 return typeManager.hasType(typeName); 206 } 207 208 protected DocumentModel getCurrentItem() { 209 return navigationContext.getCurrentDocument(); 210 } 211 212}