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