1 /* 2 * ModeShape (http://www.modeshape.org) 3 * See the COPYRIGHT.txt file distributed with this work for information 4 * regarding copyright ownership. Some portions may be licensed 5 * to Red Hat, Inc. under one or more contributor license agreements. 6 * See the AUTHORS.txt file in the distribution for a full listing of 7 * individual contributors. 8 * 9 * ModeShape is free software. Unless otherwise indicated, all code in ModeShape 10 * is licensed to you under the terms of the GNU Lesser General Public License as 11 * published by the Free Software Foundation; either version 2.1 of 12 * the License, or (at your option) any later version. 13 * 14 * ModeShape is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this software; if not, write to the Free 21 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 */ 24 package org.modeshape.graph.connector.path; 25 26 import net.jcip.annotations.ThreadSafe; 27 import org.modeshape.graph.ExecutionContext; 28 import org.modeshape.graph.property.Path; 29 import org.modeshape.graph.property.PathFactory; 30 import org.modeshape.graph.property.Path.Segment; 31 import org.modeshape.graph.request.CreateWorkspaceRequest.CreateConflictBehavior; 32 33 /** 34 * Extension of {@link PathRepository} for repositories that support modification of nodes as well as access to the nodes. 35 */ 36 @ThreadSafe 37 public abstract class WritablePathRepository extends PathRepository { 38 39 public WritablePathRepository( PathRepositorySource source ) { 40 super(source); 41 } 42 43 /** 44 * Creates a new workspace with the given name containing only a root node. 45 * <p> 46 * <b>This method does NOT automatically add the newly created workspace to the {@link #workspaces workspace map} or check to 47 * see if a workspace already exists in this repository with the same name.</b> 48 * </p> 49 * 50 * @param context the context in which the workspace is to be created 51 * @param name the name of the workspace 52 * @return the newly created workspace; may not be null 53 */ 54 protected abstract WritablePathWorkspace createWorkspace( ExecutionContext context, 55 String name ); 56 57 /** 58 * Attempts to create a workspace with the given name with name-collision behavior determined by the behavior parameter. 59 * <p> 60 * This method will first check to see if a workspace already exists with the given name. If no such workspace exists, the 61 * method will create a new workspace with the given name, add it to the {@code #workspaces workspaces map}, and return it. If 62 * a workspace with the requested name already exists and the {@code behavior} is {@link CreateConflictBehavior#DO_NOT_CREATE} 63 * , this method will return {@code null} without modifying the state of the repository. If a workspace with the requested 64 * name already exists and the {@code behavior} is {@link CreateConflictBehavior#CREATE_WITH_ADJUSTED_NAME}, this method will 65 * generate a unique new name for the workspace, create a new workspace with the given name, added it to the {@code 66 * #workspaces workspaces map}, and return it. 67 * 68 * @param context the context in which the workspace is to be created; may not be null 69 * @param name the requested name of the workspace. The name of the workspace that is returned from this method may not be the 70 * same as the requested name; may not be null 71 * @param behavior the behavior to use in case a workspace with the requested name already exists in the repository 72 * @return the newly created workspace or {@code null} if a workspace with the requested name already exists in the repository 73 * and {@code behavior == CreateConflictBehavior#DO_NOT_CREATE}. 74 */ 75 public WritablePathWorkspace createWorkspace( ExecutionContext context, 76 String name, 77 CreateConflictBehavior behavior ) { 78 String newName = name; 79 boolean conflictingName = workspaces.containsKey(newName); 80 if (conflictingName) { 81 switch (behavior) { 82 case DO_NOT_CREATE: 83 return null; 84 case CREATE_WITH_ADJUSTED_NAME: 85 int counter = 0; 86 do { 87 newName = name + (++counter); 88 } while (workspaces.containsKey(newName)); 89 break; 90 } 91 } 92 assert workspaces.containsKey(newName) == false; 93 94 WritablePathWorkspace workspace = createWorkspace(context, name); 95 workspaces.put(name, workspace); 96 return workspace; 97 } 98 99 /** 100 * Attempts to create a workspace with the requested name as in the 101 * {@link #createWorkspace(ExecutionContext, String, CreateConflictBehavior)} method and then clones the content from the 102 * given source workspace into the new workspace if the creation was successful. 103 * <p> 104 * If no workspace with the name {@code nameOfWorkspaceToClone} exists, the method will return an empty workspace. 105 * </p> 106 * 107 * @param context the context in which the workspace is to be created; may not be null 108 * @param name the requested name of the workspace. The name of the workspace that is returned from this method may not be the 109 * same as the requested name; may not be null 110 * @param existingWorkspaceBehavior the behavior to use in case a workspace with the requested name already exists in the 111 * repository 112 * @param nameOfWorkspaceToClone the name of the workspace from which the content should be cloned; may not be null 113 * @return the newly created workspace with an exact copy of the contents from the workspace named {@code 114 * nameOfWorkspaceToClone} or {@code null} if a workspace with the requested name already exists in the repository and 115 * {@code behavior == CreateConflictBehavior#DO_NOT_CREATE}. 116 */ 117 public WritablePathWorkspace createWorkspace( ExecutionContext context, 118 String name, 119 CreateConflictBehavior existingWorkspaceBehavior, 120 String nameOfWorkspaceToClone ) { 121 WritablePathWorkspace workspace = createWorkspace(context, name, existingWorkspaceBehavior); 122 if (workspace == null) { 123 // Unable to create because of a duplicate name ... 124 return null; 125 } 126 PathWorkspace original = getWorkspace(nameOfWorkspaceToClone); 127 128 PathFactory pathFactory = context.getValueFactories().getPathFactory(); 129 Path rootPath = pathFactory.createRootPath(); 130 131 if (original != null) { 132 // Copy the properties of the root node ... 133 PathNode root = workspace.getNode(rootPath); 134 PathNode origRoot = original.getNode(rootPath); 135 workspace.removeProperties(context, rootPath, root.getProperties().keySet()); 136 workspace.setProperties(context, rootPath, origRoot.getProperties()); 137 138 // Loop over each child and call this method to copy the immediate children (and below). 139 for (Segment childSegment : origRoot.getChildSegments()) { 140 Path childPath = pathFactory.create(origRoot.getPath(), childSegment); 141 PathNode originalNode = original.getNode(childPath); 142 143 workspace.copyNode(context, originalNode, original, root, childSegment.getName(), true); 144 } 145 } 146 147 workspaces.put(name, workspace); 148 return workspace; 149 } 150 151 /** 152 * Removes the named workspace from the {@code #workspaces workspaces map}. 153 * 154 * @param name the name of the workspace to remove 155 * @return {@code true} if a workspace with that name previously existed in the map 156 */ 157 public boolean destroyWorkspace( String name ) { 158 return workspaces.remove(name) != null; 159 } 160 161 @Override 162 public boolean isWritable() { 163 return true; 164 } 165 166 }