001/* 002 * Copyright 2011 Anyware Services 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 */ 016package org.ametys.workspaces.repository.jcr; 017 018import java.util.Collections; 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.Map; 022import java.util.Set; 023 024import javax.jcr.RepositoryException; 025import javax.jcr.Session; 026import javax.jcr.Workspace; 027import javax.jcr.nodetype.NodeDefinition; 028import javax.jcr.nodetype.NodeType; 029import javax.jcr.nodetype.NodeTypeIterator; 030import javax.jcr.nodetype.NodeTypeManager; 031 032import org.apache.avalon.framework.component.Component; 033import org.apache.avalon.framework.context.Context; 034import org.apache.avalon.framework.context.ContextException; 035import org.apache.avalon.framework.context.Contextualizable; 036import org.apache.avalon.framework.logger.AbstractLogEnabled; 037import org.apache.avalon.framework.service.ServiceException; 038import org.apache.avalon.framework.service.ServiceManager; 039import org.apache.avalon.framework.service.Serviceable; 040import org.apache.cocoon.components.ContextHelper; 041import org.apache.cocoon.environment.Request; 042 043import org.ametys.plugins.repositoryapp.RepositoryProvider; 044 045/** 046 * Node type hierarchy component. 047 */ 048public class NodeTypeHierarchyComponent extends AbstractLogEnabled implements Component, Contextualizable, Serviceable 049{ 050 051 /** The avalon role. */ 052 public static final String ROLE = NodeTypeHierarchyComponent.class.getName(); 053 054 /** The subtypes registry, by workspace. */ 055 protected Map<String, Map<String, Set<String>>> _subTypes; 056 057 /** The repository provider. */ 058 protected RepositoryProvider _repositoryProvider; 059 060 /** The avalon context. */ 061 protected Context _context; 062 063 /** The service manager. */ 064 protected ServiceManager _manager; 065 066 @Override 067 public void contextualize(Context context) throws ContextException 068 { 069 this._context = context; 070 } 071 072 @Override 073 public void service(ServiceManager manager) throws ServiceException 074 { 075 _manager = manager; 076 _repositoryProvider = (RepositoryProvider) manager.lookup(RepositoryProvider.ROLE); 077 } 078 079 /** 080 * Get the sub types of a given node type in a specific workspace. 081 * @param nodeType the node type. 082 * @param workspaceName the workspace name. 083 * @return the sub types of the node type in the given workspace. 084 * @throws RepositoryException if an error occurred 085 */ 086 public Set<String> getDirectSubTypes(NodeType nodeType, String workspaceName) throws RepositoryException 087 { 088 if (_subTypes == null || !_subTypes.containsKey(workspaceName)) 089 { 090 _buildTypeHierarchy(workspaceName); 091 } 092 093 if (_subTypes.get(workspaceName).containsKey(nodeType.getName())) 094 { 095 return Collections.unmodifiableSet(_subTypes.get(workspaceName).get(nodeType.getName())); 096 } 097 else 098 { 099 return Collections.emptySet(); 100 } 101 } 102 103 /** 104 * Get the sub types of a given node type in a specific workspace. 105 * @param nodeType the node type. 106 * @param workspaceName the workspace name. 107 * @return the sub types of the node type in the given workspace. 108 * @throws RepositoryException if an error occurred 109 */ 110 public Set<String> getRecursiveSubTypes(String nodeType, String workspaceName) throws RepositoryException 111 { 112 if (_subTypes == null || !_subTypes.containsKey(workspaceName)) 113 { 114 _buildTypeHierarchy(workspaceName); 115 } 116 117 Set<String> allTypes = new HashSet<>(); 118 119 if (_subTypes.get(workspaceName).containsKey(nodeType)) 120 { 121 for (String subType : _subTypes.get(workspaceName).get(nodeType)) 122 { 123 allTypes.add(subType); 124 allTypes.addAll(getRecursiveSubTypes(subType, workspaceName)); 125 } 126 } 127 128 return allTypes; 129 } 130 131 /** 132 * Get the available children types for a given node type in a workspace. 133 * @param nodeDef the node definition. 134 * @param workspaceName the workspace name. 135 * @return the available node types. 136 * @throws RepositoryException if an error occurred 137 */ 138 public Set<String> getAvailableChildrenTypes(NodeDefinition nodeDef, String workspaceName) throws RepositoryException 139 { 140 if (_subTypes == null || !_subTypes.containsKey(workspaceName)) 141 { 142 _buildTypeHierarchy(workspaceName); 143 } 144 145 Set<String> types = new HashSet<>(); 146 147 NodeType[] requiredTypes = nodeDef.getRequiredPrimaryTypes(); 148 if (requiredTypes.length > 0) 149 { 150 types.add(requiredTypes[0].getName()); 151 types.addAll(getRecursiveSubTypes(requiredTypes[0].getName(), workspaceName)); 152 } 153 154 for (int i = 1; i < requiredTypes.length; i++) 155 { 156 types.add(requiredTypes[0].getName()); 157 types.retainAll(getRecursiveSubTypes(requiredTypes[0].getName(), workspaceName)); 158 } 159 160 return types; 161 } 162 163 /** 164 * Compute the primary type hierarchy. 165 * @param workspaceName The workspace name to use 166 * @throws RepositoryException if an error occurs. 167 */ 168 protected void _buildTypeHierarchy(String workspaceName) throws RepositoryException 169 { 170 if (_subTypes == null) 171 { 172 _subTypes = new HashMap<>(); 173 } 174 175 Map<String, Set<String>> workspaceTypes = new HashMap<>(); 176 177 _subTypes.put(workspaceName, workspaceTypes); 178 179 Request request = ContextHelper.getRequest(_context); 180 181 try 182 { 183 Workspace workspace = _getWorkspace(request, workspaceName); 184 NodeTypeManager ntManager = workspace.getNodeTypeManager(); 185 186 if (getLogger().isDebugEnabled()) 187 { 188 getLogger().debug("Computing node type hierarchy for workspace " + workspaceName); 189 } 190 191 NodeTypeIterator nodeTypes = ntManager.getPrimaryNodeTypes(); 192 while (nodeTypes.hasNext()) 193 { 194 NodeType nodeType = nodeTypes.nextNodeType(); 195 for (NodeType supertype : nodeType.getSupertypes()) 196 { 197 if (!supertype.isMixin()) 198 { 199 Set<String> subtypes = null; 200 if (workspaceTypes.containsKey(supertype.getName())) 201 { 202 subtypes = workspaceTypes.get(supertype.getName()); 203 } 204 else 205 { 206 subtypes = new HashSet<>(); 207 workspaceTypes.put(supertype.getName(), subtypes); 208 } 209 210 if (getLogger().isDebugEnabled()) 211 { 212 getLogger().debug(nodeType.getName() + " is a sub type of " + supertype.getName()); 213 } 214 215 subtypes.add(nodeType.getName()); 216 } 217 } 218 } 219 } 220 catch (ServiceException e) 221 { 222 throw new RuntimeException(e); 223 } 224 } 225 226 /** 227 * Get the repository and session and set the _repository and _session protected fields 228 * @param request The request 229 * @param workspaceName The jcr workspace 230 * @return the workspace. 231 * @throws RepositoryException if an error occured. 232 * @throws ServiceException if the repository can't be looked up. 233 */ 234 protected Workspace _getWorkspace(Request request, String workspaceName) throws RepositoryException, ServiceException 235 { 236 Session session = _repositoryProvider.getSession(workspaceName); 237 238 return session.getWorkspace(); 239 } 240 241}