New Jide License
[indepmod/experimental.git] / IndependentModeler / src / cz / cvut / promod / services / projectService / utils / ProjectServiceUtils.java
blob39f71e5f24d51e6cb1154884e1f7ff4f3227ff49
1 package cz.cvut.promod.services.projectService.utils;
3 import cz.cvut.promod.services.ModelerSession;
4 import cz.cvut.promod.services.projectService.treeProjectNode.*;
5 import cz.cvut.promod.services.projectService.ProjectService;
6 import cz.cvut.promod.plugin.notationSpecificPlugIn.notation.Notation;
8 import javax.swing.tree.TreeNode;
9 import javax.swing.tree.DefaultMutableTreeNode;
10 import javax.swing.tree.TreePath;
11 import javax.swing.tree.TreeModel;
12 import java.io.File;
13 import java.util.UUID;
15 import org.apache.log4j.Logger;
17 /**
18 * ProMod, master thesis project
19 * User: Petr Zverina, petr.zverina@gmail.com
20 * Date: 19:32:38, 12.11.2009
23 /**
24 * Methods completing the ProjectService and ProjectControlService services.
26 * Use this methods when not required functionality is in the ProjectService or ProjectControlService service.
28 public class ProjectServiceUtils {
30 private static final Logger LOG = Logger.getLogger(ProjectServiceUtils.class);
32 /* one can never give a project, diagram or subfolder name containing any of this chars */
33 public static final char[] DISALLOWES_FILE_NAME_SYMBOLS = {'\\', '/', '?', '%', '*', ':', '|', '"', '<', '>', '.'};
35 /**
36 * Gives the file extension of the file name specified by the parameter.
38 * @param fileName full file name
39 * @return extension of this file
41 public static String getFileExtension(final String fileName){
42 if(fileName == null){
43 return null;
46 final int index = fileName.lastIndexOf(ProjectService.FILE_EXTENSION_DELIMITER);
48 if(index < 0){
49 return null;
52 return fileName.substring(index + 1, fileName.length());
56 /**
57 * Returns the file extension preceding by the '.' symbol that has been assigned to the notation
59 * @param notationIdentifier is the notation identifier
60 * @return symbol '.' and the file extension associated with the notation specified by the notation identifier
62 public static String getNotationFileExtension(final String notationIdentifier) {
63 return ProjectService.FILE_EXTENSION_DELIMITER + ModelerSession.getNotationService().getNotation(notationIdentifier).getLocalIOController().getNotationFileExtension();
67 /**
68 * Check whether file with given name ends with any notation file extension.
70 * @param fileName is a file name with it's extension
71 * @return true if there is a notation that saves/loads files with files extension
73 public static boolean hasNotationFileExtension(final String fileName) {
74 for(final Notation notation : ModelerSession.getNotationService().getNotations()){
75 final String fileExtension = ProjectService.FILE_EXTENSION_DELIMITER + notation.getLocalIOController().getNotationFileExtension();
77 if(fileName.endsWith(fileExtension)){
78 return true;
82 return false;
86 /**
87 * Removes any file or directory (even not empty) from the file system.
89 * @param path is path to the file or directory to be deleted
91 * @return true if the job has been done successfully, false otherwise
93 public static boolean removeDirectory(final File path) {
94 if (path.isDirectory()) {
96 final String[] dirChildren = path.list();
98 for (final String dirChild : dirChildren) {
99 if (!removeDirectory(new File(path, dirChild))) { //delete sub-directories
100 return false;
105 return path.delete();
110 * Checks if there is no duplicity in display names in one project tree level.
112 * @param parent defines the required level
113 * @param displayName name of project item
114 * @return true if there is no same display name in the level
116 public static boolean isUniqueProjectContainerDisplayName(final TreeNode parent, final String displayName) {
117 for(int i = 0; i < parent.getChildCount(); i++){
119 final ProjectItem projectItem = (ProjectItem) ((DefaultMutableTreeNode) parent.getChildAt(i)).getUserObject();
121 if(projectItem.getDisplayName().equals(displayName) && projectItem instanceof ProjectContainer){
122 return false;
126 return true;
130 * Checks whether user's object stored in an instance of DefaultMutableTreeNode is an instance of
131 * ProjectContainer class.
133 * @param parent is a instance of DefaultMutableTreeNode which is supposed to hold the mentioned user's object
134 * @return true if and only if the user's object is an instance of class extending Project container interface
136 public static boolean isProjectContainer(final DefaultMutableTreeNode parent){
137 final ProjectItem projectItem;
139 try{
140 projectItem = (ProjectItem) parent.getUserObject();
141 } catch (ClassCastException exception){
142 return false;
145 return projectItem != null && projectItem instanceof ProjectContainer;
150 * Checks if there is already no another diagram of the same name and notation under the tree parent.
152 * @param parent is common the tree parent
153 * @param projectDiagram is the "new" project diagram that's name and notation identifier will be tested
155 * @return true if and only of there is not any diagram with the same name and same notation under the given tree parent yet
157 public static boolean isUniqueProjectDiagramName(final TreeNode parent, final ProjectDiagram projectDiagram) {
158 for(int i = 0; i < parent.getChildCount(); i++){
160 final Object userObject = ((DefaultMutableTreeNode) parent.getChildAt(i)).getUserObject();
162 if( userObject instanceof ProjectDiagram){
163 final ProjectDiagram existingDiagram = (ProjectDiagram) userObject;
165 if((existingDiagram.getDisplayName().equals(projectDiagram.getDisplayName()))
166 && (existingDiagram.getNotationIdentifier().equals(projectDiagram.getNotationIdentifier()))){
167 return false;
172 return true;
177 * Checks whether the tree path provided as an argument is valid tree path accordingly to the tree model.
179 * @param treePath a tree path to check for validity
180 * @return true if and only if the treePath is valid, false otherwise
182 public static boolean isValidTreePath(final TreePath treePath){
183 final TreeModel treeModel = ModelerSession.getProjectControlService().getProjectTreeModel();
184 final TreeNode rootNode = (TreeNode) treeModel.getRoot();
186 if((treePath == null) || (rootNode != treePath.getPathComponent(0))){
187 return false;
190 final int pathCount = treePath.getPathCount();
192 for(int i = 1; i < pathCount; i++){
193 final Object parent = treePath.getPathComponent(i - 1);
194 final Object child = treePath.getPathComponent(i);
196 final int index = treeModel.getIndexOfChild(parent, child);
198 if(index < 0){
199 return false;
202 final DefaultMutableTreeNode node;
203 try{
204 node = (DefaultMutableTreeNode) child;
205 } catch (ClassCastException exception){
206 LOG.error("All project tree nodes have to be an instance of DefaultMutableTreeNode class.");
207 return false;
210 final Object userObject = node.getUserObject();
212 //second node has to represent a project root
213 if(i == 1){
214 if(!(userObject instanceof ProjectRoot)){
215 return false;
219 if(userObject instanceof ProjectRoot){
220 if(i != 1){
221 return false;
223 } else if(userObject instanceof ProjectSubFolder){
224 // do nothing
225 } else if(userObject instanceof ProjectDiagram){
226 if(i != (pathCount - 1)){
227 // diagram node has no other children
228 return false;
233 return true;
238 * Looks for the tree path of a project diagram specified by the it's uuid. The search starts by the project node
239 * specified by the treePath argument and continues recursively like DFS search.
241 * Due to performance reason this method doesn't check the given tree path for validity. Programmer should do
242 * it on his/her own (using ProjectServiceUtils.isValidTreePath() method).
244 * @param treePath is the tree path of the node, where the search is supposed to start
245 * @param uuid is the uuid of searched project diagram
247 * @return tree path to the project diagram with given uuid, null if no diagram was found
249 public static TreePath findProjectDiagram(final TreePath treePath, final UUID uuid) {
251 final DefaultMutableTreeNode parentNode;
252 try{
253 parentNode = (DefaultMutableTreeNode) treePath.getLastPathComponent();
254 } catch (Exception e){
255 LOG.error("All items in the project tree modelFactory have to be an instance of DefaultMutableTreeNode class or " +
256 "any class extending DefaultMutableTreeNode class.", e);
257 return null;
260 if(parentNode.getUserObject() instanceof ProjectDiagram){
261 if(((ProjectDiagram) parentNode.getUserObject()).getUuid().equals(uuid)){
262 return treePath;
265 } else if(parentNode.getUserObject() instanceof ProjectContainer){
267 // is not a ProjectDiagram but a ProjectContainer
268 TreePath resultTreePath;
270 for(int i = 0; i < parentNode.getChildCount(); i++){
271 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) parentNode.getChildAt(i);
273 final TreePath childTreePath = treePath.pathByAddingChild(childNode);
275 resultTreePath = findProjectDiagram(childTreePath, uuid);
277 if(resultTreePath != null){
278 return resultTreePath;
282 } else {
283 LOG.error("Project's node user's object has to be an instance of ProjectDiagram or ProjectContainer interface.");
284 return null;
287 return null;
291 * Finds tree path to the child node of a node specified by parentTreePath which is one of method's arguments.
292 * This child node has to have the same name and event the same notation. If the first method argument is a
293 * directory, that method compares just display name and the directory name. If there is a real file specified by
294 * the file argument, that this method compares display name of project item and name of the file without file
295 * extension and project diagram notation and the notation that is specified by the file's extension.
297 * @param file that specifies the directory or project diagram of any notation
298 * @param parentTreePath specifies the path to the parent node where the search is supposed to be performed
299 * @return tree path of the existing project subfolder or project diagram if exists, null otherwise
301 public static TreePath findTreePath(final File file, final TreePath parentTreePath) {
302 final String name = file.getName();
304 try{
305 final DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parentTreePath.getLastPathComponent();
307 if(file.isFile() && ProjectServiceUtils.hasNotationFileExtension(name)){
309 final String fileNameWithoutExt = name.substring(0, name.lastIndexOf(ProjectService.FILE_EXTENSION_DELIMITER));
310 final String extension = name.substring(name.lastIndexOf(ProjectService.FILE_EXTENSION_DELIMITER) + 1, name.length());
311 final String notationIdentifier = ModelerSession.getNotationService().getNotationIdentifier(extension);
313 if(notationIdentifier == null){ // there is no such a notation for this file extension
314 return null;
317 if(fileNameWithoutExt == null || fileNameWithoutExt.equals("")){
318 return null;
321 for(int i = 0; i < parentNode.getChildCount(); i++){
322 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) parentNode.getChildAt(i);
324 final ProjectItem usersProjectItem = (ProjectItem) childNode.getUserObject();
326 if(usersProjectItem instanceof ProjectDiagram){
327 final ProjectDiagram projectDiagram = (ProjectDiagram) usersProjectItem;
329 if(projectDiagram.getDisplayName().equals(fileNameWithoutExt) && notationIdentifier.equals(projectDiagram.getNotationIdentifier())){
330 return parentTreePath.pathByAddingChild(childNode);
335 } else if (file.isDirectory()){
336 for(int i = 0; i < parentNode.getChildCount(); i++){
337 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) parentNode.getChildAt(i);
339 final ProjectItem usersProjectItem = (ProjectItem) childNode.getUserObject();
341 if(usersProjectItem instanceof ProjectContainer){
342 if(usersProjectItem.getDisplayName().equals(name))
343 return parentTreePath.pathByAddingChild(childNode);
348 } catch(ClassCastException exception){
349 LOG.error("An node in project tree modelFactory is not an instance of DefaultMutableTreeNode.");
352 return null;
357 * Creating a file system path of the file that's is related to the project item.
358 * If a project root is specified by the tree path and the project file (.pmp) is
359 * saved in C:\Project\myProject.pmp, then this method returns C:\Project. This
360 * works in the same way for project subfolders and diagrams. Files related to the
361 * project diagrams have an extension associated with a notation loaded.
363 * @param treePath to the project item, has to have at least 2 components
365 * @return a file system path, null if any error has occurred
367 public static String getFileSystemPathToProjectItem(final TreePath treePath){
368 if(!isValidTreePath(treePath)){
369 return null;
372 final StringBuffer fileSystemPath = new StringBuffer();
374 try{
375 ProjectRoot projectRoot = (ProjectRoot) ((DefaultMutableTreeNode)treePath.getPathComponent(1)).getUserObject();
377 fileSystemPath.append(projectRoot.getProjectLocation());
379 final int pathCount = treePath.getPathCount();
381 for(int i = 2; i < pathCount; i++){
382 final Object object = ((DefaultMutableTreeNode)treePath.getPathComponent(i)).getUserObject();
384 if(object instanceof ProjectSubFolder){
385 final String name = ((ProjectSubFolder)object).getDisplayName();
387 if(name == null || name.equals("")){
388 return null;
391 fileSystemPath.append(System.getProperty("file.separator"));
392 fileSystemPath.append(name);
394 } else if(object instanceof ProjectDiagram){
395 final ProjectDiagram projectDiagram = (ProjectDiagram)object;
396 final String name = projectDiagram.getDisplayName();
397 final String extension = getNotationFileExtension(projectDiagram.getNotationIdentifier());
399 if(name == null || name.equals("") || extension == null || extension.equals("")){
400 return null;
403 fileSystemPath.append(System.getProperty("file.separator"));
404 fileSystemPath.append(name).append(extension);
406 } else {
407 LOG.error("Any project item after project root can be only project subfolder or a project diagram.");
408 return null;
412 return fileSystemPath.toString();
414 } catch (Exception exception){
415 LOG.error("An error during file system path sequence creation has occurred.", exception);
418 return null;
422 * @param node is an instance od DefaultMutableTreeNode
423 * @return true if the node holds and instance of ProjectDiagram, false otherwise
425 public static boolean isProjectDiagram(final DefaultMutableTreeNode node){
426 return node != null && isProjectDiagram(node.getUserObject());
431 * @param object an object
432 * @return true if the object is an instance of ProjectDiagram
434 public static boolean isProjectDiagram(final Object object){
435 return object instanceof ProjectDiagram;
440 * @param node is an instance od DefaultMutableTreeNode
441 * @return true if the node holds and instance of ProjectSubFolder, false otherwise
443 public static boolean isProjectSubfolder(final DefaultMutableTreeNode node){
444 return node != null && isProjectSubfolder(node.getUserObject());
449 * @param object an object
450 * @return true if the object is an instance of ProjectSubFolder
452 public static boolean isProjectSubfolder(final Object object){
453 return object instanceof ProjectSubFolder;
457 * @param node is an instance od DefaultMutableTreeNode
458 * @return true if the node holds and instance of ProjectRoot , false otherwise
460 public static boolean isProjectRoot(final DefaultMutableTreeNode node){
461 return node != null && isProjectRoot(node.getUserObject());
466 * @param object an object
467 * @return true if the object is an instance of ProjectRoot
469 public static boolean isProjectRoot(final Object object){
470 return object instanceof ProjectRoot;
475 * Deletes an item (file or directory) from file system which path is specified by the projectParentNodeTreePath variable. The path is build
476 * from the project root file (absolut path) and then by adding subfolders names. All user's object in nodes in the
477 * tree path has to be implementations of ProjectContainer interface, except the first one, which is the model root.
478 * Finally the itemName variable is added to the end of the built path. And the item with the resulting path is deleted.
480 * @param projectParentNodeTreePath is a tree path to build the file system path the the item that is supposed to be deleted
481 * @param itemName is a name of the item to be deleted (e.g. myfile.pmd, mydir, ...)
483 * @return true on success, false otherwise
485 public static boolean deleteFSItem(final TreePath projectParentNodeTreePath, final String itemName) {
486 if(!ProjectServiceUtils.isValidTreePath(projectParentNodeTreePath) || projectParentNodeTreePath.getPathCount() < 2){
487 return false;
490 File file;
491 try{
492 ProjectRoot projectRoot = (ProjectRoot) ((DefaultMutableTreeNode) projectParentNodeTreePath.getPathComponent(1)).getUserObject();
493 file = new File((projectRoot.getProjectLocation()));
495 for(int i = 2; i < projectParentNodeTreePath.getPathCount(); i++){
496 ProjectContainer projectContainer = (ProjectContainer) ((DefaultMutableTreeNode) projectParentNodeTreePath.getPathComponent(i)).getUserObject();
498 file = new File(file.getAbsolutePath(), projectContainer.getDisplayName());
501 } catch (ClassCastException exception){
502 LOG.error("Not valid tree path or even project structure. The path do the file system item is not possible to build.", exception);
503 return false;
506 file = new File(file.getAbsolutePath(), itemName);
508 return !file.exists() || ProjectServiceUtils.removeDirectory(file);
513 * Compares two tree paths and returns true if the path specified by the second method's parameter is an
514 * extension of the path specified by the first argument of the method.
516 * The path specified by the second argument is checked for validity,
517 * @see ProjectServiceUtils isValidTreePath(final TreePath treePath)
519 * Reference comparison is used.
521 * @param originalTreePath is the shorter path
522 * @param extendedTreePath is the extended path
524 * @return true if the extended path is the same like the original path or it extends the original path and the
525 * extended path is valid tree path (with regard to the project tree model), false otherwise
527 public static boolean isTreePathExtension(final TreePath originalTreePath, final TreePath extendedTreePath){
528 if(!isValidTreePath(extendedTreePath)){
529 return false;
532 if(originalTreePath.getPathCount() > extendedTreePath.getPathCount()){
533 return false;
536 for(int i = 0; i < originalTreePath.getPathCount(); i++){
537 if(originalTreePath.getPathComponent(i) != extendedTreePath.getPathComponent(i)){
538 return false;
542 return true;
547 * Checks whether the name given as a method's argument doesn't contain any of disallowed symbols.
548 * This should ensure that all project's, subfolder's and diagram's names used in ProMod will be then
549 * possible to save on file system in main OSs (MS Windows, Linux, Solaris).
551 * @param name to be checked
552 * @return true if and only if the name given as argument doesn't contain any disallowed symbol, false otherwise
554 public static boolean isSyntacticallyCorrectName(final String name){
555 if(name == null || name.isEmpty()){
556 return false;
559 for(final char ch : DISALLOWES_FILE_NAME_SYMBOLS){
560 if(name.indexOf(ch) > -1){
561 return false;
565 return true;
569 * Returns the string of disallowed symbols as a string using specified delimiter, e.g. (delimiter is ',')
570 * ?,:,|,\,/ etc.
572 * @param delimiter is the separator
573 * @return the string of all disallowed symbols
575 public static String getDisallowedNameSymbols(final char delimiter){
576 final StringBuffer symbols = new StringBuffer();
578 final int symbolCount = DISALLOWES_FILE_NAME_SYMBOLS.length;
579 for(int i = 0; i < symbolCount; i++){
580 final char symbol = DISALLOWES_FILE_NAME_SYMBOLS[i];
582 symbols.append(symbol);
584 if(i != (symbolCount - 1)){
585 symbols.append(delimiter);
589 return symbols.toString();