View Javadoc

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 }