svn commit: r17979 - trunk/src/argouml-app: src/org/argouml/profile src/org/argouml/profile/internal tests/org/argouml tests/org/argouml/kernel tests/org/argouml/profile tests/org/argouml/profile/internal

Luis Sergio Oliveira <[email protected]>
Newsgroups gmane.comp.lang.uml.argouml.cvs
Message-ID <[email protected]>
Author: euluis
Date: 2010-02-10 17:24:27-0800
New Revision: 17979

Added:
   trunk/src/argouml-app/src/org/argouml/profile/internal/DependencyChecker.java   (contents, props changed)
   trunk/src/argouml-app/src/org/argouml/profile/internal/DependencyResolver.java   (contents, props changed)
   trunk/src/argouml-app/tests/org/argouml/TestFileHelper.java   (contents, props changed)
   trunk/src/argouml-app/tests/org/argouml/profile/internal/TestDependencyResolver.java   (contents, props changed)
Modified:
   trunk/src/argouml-app/src/org/argouml/profile/UserProfileReference.java
   trunk/src/argouml-app/src/org/argouml/profile/internal/ProfileManagerImpl.java
   trunk/src/argouml-app/tests/org/argouml/FileHelper.java
   trunk/src/argouml-app/tests/org/argouml/kernel/TestProjectWithProfiles.java
   trunk/src/argouml-app/tests/org/argouml/profile/ProfileMother.java
   trunk/src/argouml-app/tests/org/argouml/profile/TestProfileMother.java
   trunk/src/argouml-app/tests/org/argouml/profile/TestUserDefinedProfile.java
   trunk/src/argouml-app/tests/org/argouml/profile/internal/TestProfileManagerImpl.java

Log:
issue 4997: implemented reactive and smart dependency resolution of user defined profiles

Modified: trunk/src/argouml-app/src/org/argouml/profile/UserProfileReference.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/profile/UserProfileReference.java?view=diff&pathrev=17979&r1=17978&r2=17979
==============================================================================
--- trunk/src/argouml-app/src/org/argouml/profile/UserProfileReference.java	(original)
+++ trunk/src/argouml-app/src/org/argouml/profile/UserProfileReference.java	2010-02-10 17:24:27-0800
@@ -1,6 +1,6 @@
 /* $Id$
  *****************************************************************************
- * Copyright (c) 2009 Contributors - see below
+ * Copyright (c) 2009-2010 Contributors - see below
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -49,7 +49,11 @@
  */
 public class UserProfileReference extends ProfileReference {
 
-    static final String DEFAULT_USER_PROFILE_BASE_URL = 
+    /**
+     * The default user profile base URL, which will be used if no explicit URL
+     * is specified.
+     */
+    public static final String DEFAULT_USER_PROFILE_BASE_URL = 
         "http://argouml.org/user-profiles/";
 
     /**
@@ -76,5 +80,4 @@
         super(path, 
             new URL(DEFAULT_USER_PROFILE_BASE_URL + new File(path).getName()));
     }
-
 }

Added: trunk/src/argouml-app/src/org/argouml/profile/internal/DependencyChecker.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/profile/internal/DependencyChecker.java?view=markup&pathrev=17979
==============================================================================
--- (empty file)
+++ trunk/src/argouml-app/src/org/argouml/profile/internal/DependencyChecker.java	2010-02-10 17:24:27-0800
@@ -0,0 +1,33 @@
+/* $Id$
+ *****************************************************************************
+ * Copyright (c) 2010 Contributors - see below
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    euluis
+ *****************************************************************************
+ */
+
+package org.argouml.profile.internal;
+
+/**
+ * An interface to be used to check if all dependencies of items are resolved
+ * or not.
+ * @author Luis Sergio Oliveira (euluis)
+ * @param <T> the type of the items for which the dependencies are to be
+ *            checked.
+ */
+interface DependencyChecker<T> {
+    /**
+     * Check if all dependencies of item are resolved.
+     *
+     * @param item the item for which to check if the dependencies are
+     *             resolved.
+     * @return true if the check if item dependencies are all resolved is
+     *         successful.
+     */
+    boolean check(T item);
+}

Added: trunk/src/argouml-app/src/org/argouml/profile/internal/DependencyResolver.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/profile/internal/DependencyResolver.java?view=markup&pathrev=17979
==============================================================================
--- (empty file)
+++ trunk/src/argouml-app/src/org/argouml/profile/internal/DependencyResolver.java	2010-02-10 17:24:27-0800
@@ -0,0 +1,114 @@
+/* $Id$
+ *****************************************************************************
+ * Copyright (c) 2010 Contributors - see below
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    euluis
+ *****************************************************************************
+ */
+
+package org.argouml.profile.internal;
+
+import java.util.HashSet;
+import java.util.Collection;
+
+import org.apache.log4j.Logger;
+
+/**
+ * A dependency resolver for items of type T. It implements a state-full
+ * dependency resolution algorithm.
+ *
+ * @author Luis Sergio Oliveira (euluis)
+ * @param <T> the type of items for which dependencies will be resolved.
+ */
+class DependencyResolver<T> {
+    
+    private static final Logger LOG = Logger.getLogger(
+            DependencyResolver.class);
+
+    private DependencyChecker<T> checker;
+    private Collection<T> unresolvedItems;
+
+    /**
+     * Create a dependency resolver and initialize it with the associated
+     * dependency checker.
+     *
+     * @param checker the object that will be invoked to check if for a certain
+     *                item all dependencies are resolved.
+     */
+    DependencyResolver(DependencyChecker<T> checker) {
+        this.checker = checker;
+        unresolvedItems = new HashSet<T>();
+    }
+
+    /**
+     * Attempt to resolve the dependencies of the items already handed over to
+     * the resolver instance.
+     */
+    void resolve() {
+        if (unresolvedItems.isEmpty()) {
+            return;
+        }
+        resolve(new HashSet<T>());
+    }
+
+    /**
+     * @param items additional items to resolve.
+     */
+    void resolve(Collection<T> items) {
+        if (unresolvedItems.isEmpty() && items.isEmpty()) {
+            return;
+        }
+        Collection<T> allUnresolvedItems = new HashSet<T>();
+        allUnresolvedItems.addAll(items);
+        allUnresolvedItems.addAll(unresolvedItems);
+        if (LOG.isDebugEnabled()) {
+            LOG.debug(items2Msg("Attempt to resolve the following items:",
+                allUnresolvedItems));
+        }
+        Collection<T> resolved = internalResolve(allUnresolvedItems);
+        allUnresolvedItems.removeAll(resolved);
+        unresolvedItems.addAll(allUnresolvedItems);
+        if (!unresolvedItems.isEmpty()) {
+            LOG.warn(items2Msg(
+                "The following items were left unresolved after attempt:\n",
+                unresolvedItems));
+        }
+    }
+
+    private String items2Msg(String preface, Collection<T> items) {
+        StringBuffer msg = new StringBuffer(preface);
+        for (T item : items) {
+            msg.append("\t");
+            msg.append(item.toString());
+            msg.append("\n");
+        }
+        return msg.toString();
+    }
+
+    /**
+     * Recursively resolve all dependencies. Stops when an iteration through
+     * all unresolved items didn't manage to resolve any.
+     * 
+     * @param items items to resolve.
+     * @return the items that were resolved.
+     */
+    private Collection<T> internalResolve(Collection<T> items) {
+        Collection<T> resolved = new HashSet<T>();
+        for (T item : items) {
+            if (checker.check(item)) {
+                resolved.add(item);
+            }
+        }
+        HashSet<T> toResolveItems = new HashSet<T>(items);
+        toResolveItems.removeAll(resolved);
+        if (!resolved.isEmpty()) {
+            resolved.addAll(internalResolve(toResolveItems));
+        }
+        return resolved;
+    }
+}

Modified: trunk/src/argouml-app/src/org/argouml/profile/internal/ProfileManagerImpl.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/profile/internal/ProfileManagerImpl.java?view=diff&pathrev=17979&r1=17978&r2=17979
==============================================================================
--- trunk/src/argouml-app/src/org/argouml/profile/internal/ProfileManagerImpl.java	(original)
+++ trunk/src/argouml-app/src/org/argouml/profile/internal/ProfileManagerImpl.java	2010-02-10 17:24:27-0800
@@ -104,6 +104,7 @@
     
     private ProfileCodeGeneration profileCodeGeneration;
 
+    private DependencyResolver<File> resolver;
 
     /**
      * Constructor - includes initialization of built-in default profiles.
@@ -117,14 +118,14 @@
             profileCodeGeneration = new ProfileCodeGeneration(
                     profileGoodPractices);
             
-            registerProfile(profileUML);
+            registerProfileInternal(profileUML);
             addToDefaultProfiles(profileUML); 
                 // the UML Profile is always present and default
             
             // register the built-in profiles
-            registerProfile(profileGoodPractices);
-            registerProfile(profileCodeGeneration);
-            registerProfile(new ProfileMeta());
+            registerProfileInternal(profileGoodPractices);
+            registerProfileInternal(profileCodeGeneration);
+            registerProfileInternal(new ProfileMeta());
 
         } catch (ProfileException e) {
             // TODO: Why is this throwing a generic runtime exception?!?!
@@ -132,14 +133,37 @@
         } finally {
             disableConfigurationUpdate = false;
         }
-
+        createUserDefinedProfilesDependencyResolver();
         loadDirectoriesFromConfiguration();
-
         refreshRegisteredProfiles();
-
         loadDefaultProfilesfromConfiguration();
     }
 
+    private void createUserDefinedProfilesDependencyResolver() {
+        final ProfileManager profileManager = this;
+        DependencyChecker<File> checker = new DependencyChecker<File>() {
+            public boolean check(File file) {
+                boolean found = findUserDefinedProfile(file) != null;
+                if (!found) {
+                    UserDefinedProfile udp = null;
+                    try {
+                        udp = new UserDefinedProfile(file, profileManager);
+                        registerProfileInternal(udp);
+                        found = true;
+                        LOG.debug("UserDefinedProfile for file "
+                            + file.getAbsolutePath() + " registered.");
+                    } catch (ProfileException e) {
+                        // if an exception is raised file is unusable
+                        LOG.info("Failed to load user defined profile "
+                            + file.getAbsolutePath() + ".", e);
+                    }
+                }
+                return found;
+            }
+        };
+        resolver = new DependencyResolver<File>(checker);
+    }
+
     private void loadDefaultProfilesfromConfiguration() {
         if (!disableConfigurationUpdate) {
             disableConfigurationUpdate = true;
@@ -147,13 +171,10 @@
             String defaultProfilesList = Configuration
                     .getString(KEY_DEFAULT_PROFILES);
             if (defaultProfilesList.equals("")) {
-                // if the list does not exist
-                // add the Java profile and the code generation and good
-                // practices profiles as default
-
+                // if the list does not exist add the code generation and
+                // good practices profiles as default
                 addToDefaultProfiles(profileGoodPractices);
                 addToDefaultProfiles(profileCodeGeneration);
-                
             } else {
                 StringTokenizer tokenizer = new StringTokenizer(
                         defaultProfilesList, DIRECTORY_SEPARATOR, false);
@@ -173,7 +194,7 @@
                             if (p == null) {
                                 try {
                                     p = new UserDefinedProfile(file, this);
-                                    registerProfile(p);
+                                    registerProfileInternal(p);
                                 } catch (ProfileException e) {
                                     LOG.error("Error loading profile: " + file,
                                             e);
@@ -190,7 +211,6 @@
                         String profileIdentifier = desc.substring(1);
                         p = lookForRegisteredProfile(profileIdentifier);
                     }
-
                     if (p != null) {
                         addToDefaultProfiles(p);
                     }
@@ -203,7 +223,6 @@
     private void updateDefaultProfilesConfiguration() {
         if (!disableConfigurationUpdate) {
             StringBuffer buf = new StringBuffer();
-            
             for (Profile p : defaultProfiles) {
                 if (p instanceof UserDefinedProfile) {
                     buf.append("U"
@@ -212,70 +231,71 @@
                 } else {
                     buf.append("C" + p.getProfileIdentifier());
                 }
-
                 buf.append(DIRECTORY_SEPARATOR);
             }
-
             Configuration.setString(KEY_DEFAULT_PROFILES, buf.toString());
         }
     }
 
     private void loadDirectoriesFromConfiguration() {
         disableConfigurationUpdate = true;
-        
         StringTokenizer tokenizer = 
             new StringTokenizer(
                     Configuration.getString(KEY_DEFAULT_DIRECTORIES), 
                     DIRECTORY_SEPARATOR, false);
-
         while (tokenizer.hasMoreTokens()) {
             searchDirectories.add(tokenizer.nextToken());
         }
-        
         disableConfigurationUpdate = false;
     }
 
     private void updateSearchDirectoriesConfiguration() {
         if (!disableConfigurationUpdate) {
             StringBuffer buf = new StringBuffer();
-
             for (String s : searchDirectories) {
                 buf.append(s).append(DIRECTORY_SEPARATOR);
             }
-
             Configuration.setString(KEY_DEFAULT_DIRECTORIES, buf.toString());
         }
     }
 
-
     public List<Profile> getRegisteredProfiles() {
         return profiles;
     }
 
-
-    public void registerProfile(Profile p) {        
+    public void registerProfile(Profile p) {
+        if (registerProfileInternal(p)) {
+            // this profile could have not been loaded when
+            // the default profile configuration 
+            // was loaded at first, so we need to do it again
+            loadDefaultProfilesfromConfiguration();
+        }
+        resolver.resolve();
+    }
+    
+    /**
+     * @param p the profile to register.
+     * @return true if there should be an attempt to load the default profiles
+     * from the configuration.
+     */
+    private boolean registerProfileInternal(Profile p) {
+        boolean loadDefaultProfilesFromConfiguration = false;
         if (p != null && !profiles.contains(p)) {
             if (p instanceof UserDefinedProfile
                     || getProfileForClass(p.getClass().getName()) == null) {
+                loadDefaultProfilesFromConfiguration = true;
                 profiles.add(p);
-
                 for (Critic critic : p.getCritics()) {
                     for (Object meta : critic.getCriticizedDesignMaterials()) {
                         Agency.register(critic, meta);
                     }
-
                     critic.setEnabled(false);
                 }
-                                                
-                // this profile could have not been loaded when 
-                // the default profile configuration 
-                // was loaded at first, so we need to do it again
-                loadDefaultProfilesfromConfiguration();
             }
         }
+        return loadDefaultProfilesFromConfiguration;
     }
 
-
     public void removeProfile(Profile p) {
         if (p != null && p != profileUML) {
             profiles.remove(p);
@@ -292,7 +312,6 @@
         }
     }
 
-
     private static final String OLD_PROFILE_PACKAGE = "org.argouml.uml.profile";
 
     private static final String NEW_PROFILE_PACKAGE = 
@@ -320,7 +339,6 @@
         return found;
     }
 
-
     public void addToDefaultProfiles(Profile p) {
         if (p != null && profiles.contains(p) 
                 && !defaultProfiles.contains(p)) {
@@ -329,12 +347,10 @@
         }
     }
 
-
     public List<Profile> getDefaultProfiles() {
         return Collections.unmodifiableList(defaultProfiles);
     }
 
-
     public void removeFromDefaultProfiles(Profile p) {
         if (p != null && p != profileUML && profiles.contains(p)) {
             defaultProfiles.remove(p);
@@ -342,7 +358,6 @@
         }
     }
 
-
     public void addSearchPathDirectory(String path) {
         if (path != null && !searchDirectories.contains(path)) {
             searchDirectories.add(path);
@@ -355,12 +370,10 @@
         }
     }
 
-
     public List<String> getSearchPathDirectories() {
         return Collections.unmodifiableList(searchDirectories);
     }
 
-
     public void removeSearchPathDirectory(String path) {
         if (path != null) {
             searchDirectories.remove(path);
@@ -374,48 +387,29 @@
     }
 
     public void refreshRegisteredProfiles() {
-
         ArrayList<File> dirs = new ArrayList<File>();
-        
         for (String dirName : searchDirectories) {
             File dir = new File(dirName);
             if (dir.exists()) {
                 dirs.add(dir);
             }
         }
-        
         if (!dirs.isEmpty()) {
             // TODO: Allow .zargo as profile as well?
-            File[] fileArray = new File[dirs.size()];
-            for (int i = 0; i < dirs.size(); i++) {
-                fileArray[i] = dirs.get(i);
-            }
-            List<File> dirList
-                = UserDefinedProfileHelper.getFileList(fileArray);
-            for (File file : dirList) {
-                boolean found = 
-                    findUserDefinedProfile(file) != null;
-                if (!found) {
-                    UserDefinedProfile udp = null;
-                    try {
-                        udp = new UserDefinedProfile(file, this);
-                        registerProfile(udp);
-                    } catch (ProfileException e) {
-                        // if an exception is raised file is unusable
-                        LOG.warn("Failed to load user defined profile "
-                            + file.getAbsolutePath() + ".", e);
-                    }
-                }
-            }
+            List<File> profileFiles = UserDefinedProfileHelper.getFileList(
+                dirs.toArray(new File[0]));
+            loadProfiles(profileFiles);
         }
     }
 
+    void loadProfiles(List<File> profileFiles) {
+        resolver.resolve(profileFiles);
+    }
+
     private Profile findUserDefinedProfile(File file) {
-        
         for (Profile p : profiles) {
             if (p instanceof UserDefinedProfile) {
                 UserDefinedProfile udp = (UserDefinedProfile) p;
-
                 if (file.equals(udp.getModelFile())) {
                     return udp;
                 }
@@ -424,7 +418,6 @@
         return null;
     }
 
-
     public Profile getUMLProfile() {
         return profileUML;
     }
@@ -455,7 +448,6 @@
                 Configuration.setBoolean(c.getCriticKey(), false);
             }
         }
-        
         for (Profile p : pc.getProfiles()) {
             for (Critic c : p.getCritics()) {
                 c.setEnabled(true);
@@ -463,5 +455,4 @@
             }
         }        
     }
-
 }

Modified: trunk/src/argouml-app/tests/org/argouml/FileHelper.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/tests/org/argouml/FileHelper.java?view=diff&pathrev=17979&r1=17978&r2=17979
==============================================================================
--- trunk/src/argouml-app/tests/org/argouml/FileHelper.java	(original)
+++ trunk/src/argouml-app/tests/org/argouml/FileHelper.java	2010-02-10 17:24:27-0800
@@ -1,6 +1,6 @@
 /* $Id$
  *****************************************************************************
- * Copyright (c) 2009 Contributors - see below
+ * Copyright (c) 2009-2010 Contributors - see below
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -39,6 +39,9 @@
 package org.argouml;
 
 import java.io.File;
+import java.io.IOException;
+
+import junit.framework.TestCase;
 
 /**
  * Helper for common File related operations used in automated tests.
@@ -46,39 +49,78 @@
  * @author Luis Sergio Oliveira (euluis)
  */
 public class FileHelper {
-
+    
     /**
-     * System temporary directory property name.
+     * Default temporary directory prefix.
      */
-    public static final String SYSPROPNAME_TMPDIR = "java.io.tmpdir";
-
-
-    public static File getTmpDir() {
-        return new File(System.getProperty(SYSPROPNAME_TMPDIR));
-    }
+    static final String DEFAULT_TEMP_DIR_PREFIX = "prefix";
 
     /**
-     * Setup a directory with the given name for the caller test.
+     * Setup a directory with the given name prefix for the caller test.
      * 
-     * @param dirName the directory to be created in the system temporary dir
-     * @return the created directory
+     * @param dirNamePrefix the prefix of the directory name to be created in
+     *        the system temporary directory.
+     * @return the created directory.
+     * @throws IOException if the directory creation fails.
      */
-    public static File setUpDir4Test(String dirName) {
-        File generationDir = new File(getTmpDir(), dirName);
-        generationDir.mkdirs();
-        return generationDir;
+    public static File setUpDir4Test(String dirNamePrefix) throws IOException {
+        return createTempDirectory(dirNamePrefix);
     }
     
-    public static File setUpDir4Test(Class<?> testClass) {
+    /**
+     * @param testClass the {@link TestCase} class for which to create a
+     *        directory.
+     * @return the created directory.
+     * @throws IOException if the directory creation fails.
+     */
+    public static File setUpDir4Test(Class<?> testClass) throws IOException {
         String name = testClass.getPackage().getName() + "." 
             + testClass.getSimpleName();
         return setUpDir4Test(name);
     }
     
-    public static void deleteDir(File dir) {
-        if (dir != null && dir.exists()) {
-            dir.delete();
+    /**
+     * Delete fileOrDirectory with the bonus of recursively deleting children
+     * of fileOrDirectory if it is a directory.
+     * 
+     * @param fileOrDirectory the file or directory to be deleted.
+     */
+    public static void delete(File fileOrDirectory) {
+        if (fileOrDirectory != null && fileOrDirectory.exists()) {
+            if (fileOrDirectory.isDirectory()) {
+                File[] children = fileOrDirectory.listFiles();
+                for (File child : children) {
+                    delete(child);
+                }
+            }
+            fileOrDirectory.delete();
         }
     }
 
+    /**
+     * @param prefix the prefix of the directory name.
+     * @return a {@link File} associated to a newly created directory which is
+     * contained within the system temporary directory.
+     * @throws IOException When the creation of the directory throws.
+     */
+    public static File createTempDirectory(String prefix) throws IOException {
+        File tempFile = File.createTempFile(prefix, "");
+        String absolutePath = tempFile.getAbsolutePath();
+        tempFile.delete();
+        tempFile.mkdir();
+        return new File(absolutePath);
+    }
+
+    /**
+     * Create a unique temporary directory contained within the system
+     * temporary directory.
+     * 
+     * @return a {@link File} associated to a newly created directory which is
+     * contained within the system temporary directory and with prefix
+     * {@link FileHelper#DEFAULT_TEMP_DIR_PREFIX}.
+     * @throws IOException When the creation of the directory throws.
+     */
+    public static File createTempDirectory() throws IOException {
+        return createTempDirectory(DEFAULT_TEMP_DIR_PREFIX);
+    }
 }

Added: trunk/src/argouml-app/tests/org/argouml/TestFileHelper.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/tests/org/argouml/TestFileHelper.java?view=markup&pathrev=17979
==============================================================================
--- (empty file)
+++ trunk/src/argouml-app/tests/org/argouml/TestFileHelper.java	2010-02-10 17:24:27-0800
@@ -0,0 +1,39 @@
+/* $Id$
+ *****************************************************************************
+ * Copyright (c) 2010 Contributors - see below
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    euluis
+ *****************************************************************************
+ */
+
+package org.argouml;
+
+import java.io.File;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+/**
+ * Integration tests for the FileHelper.
+ * @author Luis Sergio Oliveira (euluis)
+ */
+public class TestFileHelper extends TestCase {
+    
+    /**
+     * Test {@link FileHelper#createTempDirectory()} with the directory not
+     * existing before.
+     * @throws IOException if the creation of files or directories throws.
+     */
+    public void testCreateTempDirectory() throws IOException {
+        File tmpDir = FileHelper.createTempDirectory();
+        tmpDir.deleteOnExit();
+        assertTrue("The directory should exist.", tmpDir.exists());
+        assertTrue("The directory isn't a directory.", tmpDir.isDirectory());
+        assertTrue("The directory is writable.", tmpDir.canWrite());
+    }
+}

Modified: trunk/src/argouml-app/tests/org/argouml/kernel/TestProjectWithProfiles.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/tests/org/argouml/kernel/TestProjectWithProfiles.java?view=diff&pathrev=17979&r1=17978&r2=17979
==============================================================================
--- trunk/src/argouml-app/tests/org/argouml/kernel/TestProjectWithProfiles.java	(original)
+++ trunk/src/argouml-app/tests/org/argouml/kernel/TestProjectWithProfiles.java	2010-02-10 17:24:27-0800
@@ -83,6 +83,7 @@
     /*
      * @see junit.framework.TestCase#setUp()
      */
+    @SuppressWarnings("unchecked")
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -97,13 +98,13 @@
             initMethod.invoke(null);
             assertNotNull(ApplicationVersion.getVersion());
         }
-        String testCaseDirName = getClass().getPackage().getName();
-        testCaseDir = FileHelper.setUpDir4Test(testCaseDirName);
+        String testCaseDirNamePrefix = getClass().getPackage().getName();
+        testCaseDir = FileHelper.setUpDir4Test(testCaseDirNamePrefix);
     }
     
     @Override
     protected void tearDown() throws Exception {
-        FileHelper.deleteDir(testCaseDir);
+        FileHelper.delete(testCaseDir);
         super.tearDown();
     }
 

Modified: trunk/src/argouml-app/tests/org/argouml/profile/ProfileMother.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/tests/org/argouml/profile/ProfileMother.java?view=diff&pathrev=17979&r1=17978&r2=17979
==============================================================================
--- trunk/src/argouml-app/tests/org/argouml/profile/ProfileMother.java	(original)
+++ trunk/src/argouml-app/tests/org/argouml/profile/ProfileMother.java	2010-02-10 17:24:27-0800
@@ -1,6 +1,6 @@
 /* $Id$
  *****************************************************************************
- * Copyright (c) 2009 Contributors - see below
+ * Copyright (c) 2009-2010 Contributors - see below
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -8,6 +8,7 @@
  *
  * Contributors:
  *    maurelio1234
+ *    euluis
  *****************************************************************************
  *
  * Some portions of this file was previously release using the BSD License:
@@ -49,14 +50,19 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 import junit.framework.TestCase;
 
 import org.apache.log4j.Logger;
 
+import org.argouml.FileHelper;
 import org.argouml.model.Model;
+import org.argouml.model.UmlException;
+import org.argouml.model.XmiReader;
 import org.argouml.model.XmiWriter;
 import org.argouml.persistence.UmlFilePersister;
+import org.xml.sax.InputSource;
 
 /**
  * Based on the 
@@ -66,7 +72,24 @@
  * @author Luis Sergio Oliveira (euluis)
  */
 public class ProfileMother {
-    
+
+    /**
+     * This interface implementors can be used to create dependencies between
+     * profiles.
+     * @author Luis Sergio Oliveira (euluis)
+     */
+    public interface DependencyCreator {
+        /**
+         * Creates a dependency between dependentProfile and
+         * profileFromWhichDepends.
+         * @param profileFromWhichDepends the profile model to which
+         *        dependentProfile will depend.
+         * @param dependentProfile the profile model that will be dependent
+         *        of profileFromWhichDepends.
+         */
+        void create(Object profileFromWhichDepends, Object dependentProfile);
+    }
+
     private static final Logger LOG = Logger.getLogger(ProfileMother.class);
 
     /**
@@ -77,20 +100,34 @@
      * "st" the example stereotype name.
      */
     public static final String STEREOTYPE_NAME_ST = "st";
+    
+    private final String DEFAULT_SIMPLE_PROFILE_NAME = "SimpleProfile";
 
     /**
-     * Create a simple profile model with a class named "foo" and with a 
-     * stereotype named "st".
+     * Create a simple profile model with name {@link ProfileMother#DEFAULT_SIMPLE_PROFILE_NAME}
+     * with a class named "foo" and with a stereotype named
+     * {@link ProfileMother#STEREOTYPE_NAME_ST}.
      * 
      * @return the profile model.
      */
     public Object createSimpleProfileModel() {
-        Object model = getModelManagementFactory().createModel();
-        Object profileStereotype = getProfileStereotype();
-        getCoreHelper().addStereotype(model, profileStereotype);
+        return createSimpleProfileModel(DEFAULT_SIMPLE_PROFILE_NAME);
+    }
+
+    /**
+     * Create a simple profile model with name profileName,
+     * with a class named "foo" and with a stereotype named
+     * {@link ProfileMother#STEREOTYPE_NAME_ST}.
+     * 
+     * @param profileName the name that the created profile shall have.
+     * @return the profile model.
+     */
+    public Object createSimpleProfileModel(String profileName) {
+        Object model = getModelManagementFactory().createProfile();
         Object fooClass = Model.getCoreFactory().buildClass("foo", model);
         getExtensionMechanismsFactory().buildStereotype(fooClass, 
                 STEREOTYPE_NAME_ST, model);
+        getCoreHelper().setName(model, profileName);
         return model;
     }
 
@@ -151,4 +188,89 @@
         }
     }
 
+    /**
+     * Create a XMI file that stores a UML profile which depends via XMI
+     * of the profile stored in profileFromWhichDependsFile.
+     * @param profileFromWhichDependsFile a {@link File} associated to the file
+     *        which contains the XMI for the profile from which the created
+     *        profile will depend.
+     * @param dependencyCreator the object that will be called to actually
+     *        create the dependency between the new model and the other.
+     * @param profilesDir the directory within which the profile XMI file that
+     *        will be created is stored.
+     * @param dependentProfileFilenamePrefix the file name prefix for the file
+     *        that will contain the XMI of the created profile.
+     * @return the {@link File} associated to the file where the created
+     * dependent profile is stored (XMI).
+     * @throws IOException if the new profile file creation fails.
+     * @throws UmlException if the model subsystem throws.
+     */
+    public File createXmiDependentProfile(File profileFromWhichDependsFile,
+            DependencyCreator dependencyCreator,
+            File profilesDir, String dependentProfileFilenamePrefix)
+        throws IOException, UmlException {
+        XmiReader xmiReader = Model.getXmiReader();
+        xmiReader.addSearchPath(profileFromWhichDependsFile.getParent());
+        InputSource pIs = new InputSource(
+            profileFromWhichDependsFile.toURI().toURL().toExternalForm());
+        pIs.setPublicId(UserProfileReference.DEFAULT_USER_PROFILE_BASE_URL
+            + profileFromWhichDependsFile.getName());
+        Collection profileFromWhichDependsModelTopElements = xmiReader.parse(
+            pIs, true);
+        Object dependentProfile = getModelManagementFactory().createProfile();
+        Object profileFromWhichDependsModel = null;
+        for (Object topElement : profileFromWhichDependsModelTopElements) {
+            if (DEFAULT_SIMPLE_PROFILE_NAME.equals(
+                    getFacade().getName(topElement))) {
+                profileFromWhichDependsModel = topElement;
+                break;
+            }
+        }
+        dependencyCreator.create(profileFromWhichDependsModel,
+            dependentProfile);
+        File dependentProfileFile = File.createTempFile(
+            dependentProfileFilenamePrefix, ".xmi", profilesDir);
+        saveProfileModel(dependentProfile, dependentProfileFile);
+        Model.getUmlFactory().deleteExtent(dependentProfile);
+        xmiReader.removeSearchPath(profileFromWhichDependsFile.getParent());
+        return dependentProfileFile;
+    }
+
+    /**
+     * Creates two profiles with one depending of the other. Saves the two
+     * profiles in a temporary directory and returns the associated
+     * {@link File Files}, being that the second XMI file depends on the first.
+     * @return A list of two files, the second File contains an XMI that
+     *         depends of the first.
+     * @throws IOException if file IO causes errors.
+     * @throws UmlException if the manipulation of models causes errors.
+     */
+    public List<File> createProfileFilePairWithSecondDependingOnFirstThroughXmi()
+            throws IOException, UmlException {
+        File profilesDir = FileHelper.createTempDirectory();
+        final File baseFile = File.createTempFile(
+            "baseProfile", ".xmi", profilesDir);
+        Object model = createSimpleProfileModel();
+        saveProfileModel(model, baseFile);
+        Model.getUmlFactory().deleteExtent(model);
+        ProfileMother.DependencyCreator dependencyCreator =
+            new ProfileMother.DependencyCreator() {
+            public void create(Object profileFromWhichDepends,
+                    Object dependentProfile) {
+                Object theClass = Model.getCoreFactory().buildClass("DasClazz",
+                    dependentProfile);
+                Collection stereotypes = getFacade().getStereotypes(
+                    profileFromWhichDepends);
+                assert stereotypes.size() >= 1: "";
+                Object stereotype = stereotypes.iterator().next();
+                Model.getCoreHelper().addStereotype(theClass, stereotype);
+            }
+        };
+        String dependentProfileFilenamePrefix = "dependentProfile";
+        final File dependentFile = createXmiDependentProfile(
+            baseFile, dependencyCreator, profilesDir,
+            dependentProfileFilenamePrefix);
+        return new ArrayList<File>() { { add(baseFile); add(dependentFile); }
+        };
+    }
 }

Modified: trunk/src/argouml-app/tests/org/argouml/profile/TestProfileMother.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/tests/org/argouml/profile/TestProfileMother.java?view=diff&pathrev=17979&r1=17978&r2=17979
==============================================================================
--- trunk/src/argouml-app/tests/org/argouml/profile/TestProfileMother.java	(original)
+++ trunk/src/argouml-app/tests/org/argouml/profile/TestProfileMother.java	2010-02-10 17:24:27-0800
@@ -1,6 +1,6 @@
 /* $Id$
  *****************************************************************************
- * Copyright (c) 2009 Contributors - see below
+ * Copyright (c) 2009-2010 Contributors - see below
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -8,6 +8,7 @@
  *
  * Contributors:
  *    maurelio1234
+ *    euluis
  *****************************************************************************
  *
  * Some portions of this file was previously release using the BSD License:
@@ -41,23 +42,32 @@
 import static org.argouml.model.Model.getExtensionMechanismsHelper;
 import static org.argouml.model.Model.getFacade;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
+import org.argouml.FileHelper;
 import org.argouml.model.InitializeModel;
 import org.argouml.model.Model;
+import org.argouml.model.UmlException;
+import org.argouml.model.XmiReader;
+import org.xml.sax.InputSource;
 
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 /**
+ * Integration tests for the {@link ProfileMother} class.
  *
  * @author Luis Sergio Oliveira (euluis)
  */
 public class TestProfileMother extends TestCase {
     
     private ProfileMother mother;
-    private File testDir;
 
     @Override
     protected void setUp() throws Exception {
@@ -65,6 +75,9 @@
         mother = new ProfileMother();
     }
     
+    /**
+     * Test the creation of a profile model.
+     */
     public void testCreateProfileModel() {
         Object model = mother.createSimpleProfileModel();
         assertNotNull(model);
@@ -74,7 +87,11 @@
             getFacade().getName(profileStereotypes.iterator().next()));
     }
     
-    public void testCreateSimpleProfileModel() throws Exception {
+    /**
+     * Test the creation of a simple profile model and check that specific
+     * model elements are contained in it.
+     */
+    public void testCreateSimpleProfileModel() {
         final Object model = mother.createSimpleProfileModel();
         Collection<Object> models = new ArrayList<Object>() { {
                 add(model);
@@ -92,15 +109,133 @@
         }
         assertNotNull("\"st\" stereotype not found in model.", st);
         assertTrue(Model.getExtensionMechanismsHelper().isStereotype(st, 
-                ProfileMother.STEREOTYPE_NAME_ST, "Class"));
+            ProfileMother.STEREOTYPE_NAME_ST, "Class"));
     }
     
+    /**
+     * Test saving a profile model.
+     * 
+     * @throws Exception when saving the profile model fails
+     */
     public void testSaveProfileModel() throws Exception {
         Object model = mother.createSimpleProfileModel();
-        File file = new File(testDir, "testSaveProfileModel.xmi");
+        File file = File.createTempFile("testSaveProfileModel", ".xmi");
         mother.saveProfileModel(model, file);
         assertTrue("The file to where the file was supposed to be saved " 
-                + "doesn't exist.", file.exists());
+            + "doesn't exist.", file.exists());
     }
     
+    /**
+     * Test the creation of a profile which depends on another profile.
+     * Doesn't use the {@link ProfileMother#createXmiDependentProfile(File, ProfileMother.DependencyCreator, File, String)}
+     * method, but, it serves as good executable documentation of how this is
+     * done as a whole.
+     * @throws IOException When saving the profile models fails.
+     * @throws UmlException When something in the model subsystem goes wrong.
+     */
+    public void testXmiDependentProfile() throws IOException, UmlException {
+        Object model = mother.createSimpleProfileModel();
+        File file = File.createTempFile("simple-profile", ".xmi");
+        mother.saveProfileModel(model, file);
+        XmiReader xmiReader = Model.getXmiReader();
+        xmiReader.addSearchPath(file.getParent());
+        InputSource pIs = new InputSource(file.toURI().toURL().toExternalForm());
+        pIs.setPublicId(file.getName());
+        Collection simpleModelTopElements = xmiReader.parse(pIs, true);
+        Object model2 = mother.createSimpleProfileModel();
+        Object theClass = Model.getCoreFactory().buildClass("TheClass", model2);
+        Collection stereotypes = getFacade().getStereotypes(
+            simpleModelTopElements.iterator().next());
+        Object stereotype = stereotypes.iterator().next();
+        Model.getCoreHelper().addStereotype(theClass, stereotype);
+        File dependentProfileFile = File.createTempFile("dependent-profile",
+            ".xmi");
+        mother.saveProfileModel(model2, dependentProfileFile);
+        assertTrue("The file to where the file was supposed to be saved " 
+            + "doesn't exist.", dependentProfileFile.exists());
+        assertStringInLineOfFile("The name of the file which contains the profile "
+            + "from which the dependent profile depends must occur in the "
+            + "file.",
+            file.getName(), dependentProfileFile);
+    }
+    
+    /**
+     * Test the creation of a profile which depends on another profile.
+     * @throws IOException When saving the profile models fails.
+     * @throws UmlException When something in the model subsystem goes wrong.
+     */
+    public void testCreateXmiDependentProfile() throws IOException, UmlException {
+        File profilesDir = FileHelper.createTempDirectory();
+        File profileFromWhichDependsFile = File.createTempFile(
+            "simple-profile", ".xmi", profilesDir);
+        Object model = mother.createSimpleProfileModel();
+        mother.saveProfileModel(model, profileFromWhichDependsFile);
+        Model.getUmlFactory().deleteExtent(model);
+        // setting up the dependent profile creation
+        ProfileMother.DependencyCreator dependencyCreator =
+            new ProfileMother.DependencyCreator() {
+            public void create(Object profileFromWhichDepends,
+                    Object dependentProfile) {
+                Object theClass = Model.getCoreFactory().buildClass("TheClass",
+                    dependentProfile);
+                Collection stereotypes = getFacade().getStereotypes(
+                    profileFromWhichDepends);
+                Object stereotype = stereotypes.iterator().next();
+                Model.getCoreHelper().addStereotype(theClass, stereotype);
+            }
+        };
+        String dependentProfileFilenamePrefix = "dependent-profile";
+        // actual call that executes everything
+        File dependentProfileFile = mother.createXmiDependentProfile(
+            profileFromWhichDependsFile, dependencyCreator,
+            profilesDir, dependentProfileFilenamePrefix);
+        // verifications
+        assertTrue("The file to where the file was supposed to be saved " 
+            + "doesn't exist.", dependentProfileFile.exists());
+        assertStringInLineOfFile("The name of the file which contains the profile "
+            + "from which the dependent profile depends must occur in the "
+            + "file.",
+            profileFromWhichDependsFile.getName(), dependentProfileFile);
+        XmiReader xmiReader = Model.getXmiReader();
+        xmiReader.addSearchPath(profilesDir.getAbsolutePath());
+        InputSource pIs = new InputSource(
+            dependentProfileFile.toURI().toURL().toExternalForm());
+        pIs.setPublicId(dependentProfileFile.getName());
+        Collection dependentProfileModelTopElements = xmiReader.parse(pIs,
+            true);
+        assertEquals("There should exist only one top level element.",
+            1, dependentProfileModelTopElements.size());
+    }
+
+    private void assertStringInLineOfFile(String failureMsg, String str, File file)
+            throws IOException {
+        BufferedReader fileReader = new BufferedReader(new FileReader(file));
+        try {
+            String line = "";
+            while (null != (line = fileReader.readLine())) {
+                if (line.contains(str))
+                    return;
+            }
+        } finally {
+            if (fileReader != null) {
+                fileReader.close();
+            }
+        }
+        throw new AssertionFailedError(failureMsg + " '" + str
+            + "' not found in " + file.getName());
+    }
+    
+    /**
+     * Test {@link ProfileMother#createProfileFilePairWithSecondDependingOnFirstThroughXmi()}.
+     * @throws IOException when file IO goes wrong...
+     * @throws UmlException when UML manipulation goes wrong...
+     */
+    public void testCreateProfilePairWithSecondDependingOnFirstThroughXmi() throws IOException, UmlException {
+        List<File> profilesFiles =
+            mother.createProfileFilePairWithSecondDependingOnFirstThroughXmi();
+        assertEquals("Should contain two elements.", 2, profilesFiles.size());
+        File baseFile = profilesFiles.get(0);
+        File dependentFile = profilesFiles.get(1);
+        assertStringInLineOfFile("", baseFile.getName(), dependentFile);
+    }
 }

Modified: trunk/src/argouml-app/tests/org/argouml/profile/TestUserDefinedProfile.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/tests/org/argouml/profile/TestUserDefinedProfile.java?view=diff&pathrev=17979&r1=17978&r2=17979
==============================================================================
--- trunk/src/argouml-app/tests/org/argouml/profile/TestUserDefinedProfile.java	(original)
+++ trunk/src/argouml-app/tests/org/argouml/profile/TestUserDefinedProfile.java	2010-02-10 17:24:27-0800
@@ -40,6 +40,7 @@
 package org.argouml.profile;
 
 import java.io.File;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -48,6 +49,7 @@
 import org.argouml.FileHelper;
 import org.argouml.cognitive.Critic;
 import org.argouml.model.InitializeModel;
+import org.argouml.model.Model;
 import org.argouml.profile.internal.ProfileManagerImpl;
 import org.argouml.profile.internal.ocl.CrOCL;
 
@@ -66,13 +68,22 @@
         super.setUp();
         InitializeModel.initializeDefault();
         ProfileFacade.setManager(new ProfileManagerImpl());
-
+        // TODO: the following cleans up left overs from previous tests, but,
+        // preferably we shouldn't have to do this...
+        Collection rootElements = Model.getFacade().getRootElements();
+        for (Object rootElement : rootElements) {
+            if (Model.getFacade().isAModel(rootElement)
+                && "SimpleProfile".equals(Model.getFacade().getName(
+                    rootElement))) {
+                Model.getUmlFactory().deleteExtent(rootElement);
+            }
+        }
         testDir = FileHelper.setUpDir4Test(getClass());
     }
 
     @Override
     protected void tearDown() throws Exception {
-        FileHelper.deleteDir(testDir);
+        FileHelper.delete(testDir);
         super.tearDown();
     }
 
@@ -85,19 +96,20 @@
     public void testLoadingConstructor() throws Exception {
         // create profile model
         ProfileMother profileMother = new ProfileMother();
-        Object model = profileMother.createSimpleProfileModel();
+        final String profileName = "testLoadingConstructorProfile";
+        Object model = profileMother.createSimpleProfileModel(profileName);
         // save the profile into a xmi file
         File profileFile = new File(testDir, "testLoadingConstructor.xmi");
         profileMother.saveProfileModel(model, profileFile);
         Profile profile = new UserDefinedProfile(profileFile,
             ProfileFacade.getManager());
-        assertTrue(profile.getDisplayName().contains(profileFile.getName()));
+        assertEquals(profileName, profile.getDisplayName());
     }
 
     /**
-     * Test the constructor used for loading a profile from a Jar file TODO Test
-     * FigNode!
-     * 
+     * Test the constructor used for loading a profile from a Jar file.
+     * TODO: Test FigNode!
+     *
      * @throws Exception if something goes wrong
      */
     public void testLoadingAsFromJar() throws Exception {

Added: trunk/src/argouml-app/tests/org/argouml/profile/internal/TestDependencyResolver.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/tests/org/argouml/profile/internal/TestDependencyResolver.java?view=markup&pathrev=17979
==============================================================================
--- (empty file)
+++ trunk/src/argouml-app/tests/org/argouml/profile/internal/TestDependencyResolver.java	2010-02-10 17:24:27-0800
@@ -0,0 +1,259 @@
+/* $Id$
+ *****************************************************************************
+ * Copyright (c) 2010 Contributors - see below
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    euluis
+ *****************************************************************************
+ */
+
+package org.argouml.profile.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link DependencyResolver}.
+ * @author Luis Sergio Oliveira (euluis)
+ */
+public class TestDependencyResolver extends TestCase {
+    private DependencyResolver<String> resolver;
+    private DummyDependencyChecker checker;
+
+    /**
+     * @throws Exception
+     * @see junit.framework.TestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    /**
+     * Test invoke of {@link DependencyResolver#resolve()} method without any
+     * arguments.
+     */
+    public void testResolveNoArgs() {
+        DependencyChecker<String> checker2 = new DependencyChecker<String>() {
+            public boolean check(String item) {
+                return true;
+            }
+        };
+        resolver = new DependencyResolver<String>(checker2);
+        resolver.resolve();
+    }
+
+    /**
+     * Test {@link DependentString#equals(Object)}.
+     */
+    public void testDependentStringEquals() {
+        // this is neat...
+        assertEquals(new DependentString("A"), "A");
+        // but, lookout, it doesn't work both ways
+        assertFalse("A".equals(new DependentString("A")));
+    }
+
+    /**
+     * Test the resolution of several items all without any dependencies.
+     */
+    public void testResolveWithAllResolvableDependencies() {
+        final Collection<DependentString> dependentItems =
+            new ArrayList<DependentString>() { {
+                add(new DependentString("A"));
+                add(new DependentString("B"));
+                add(new DependentString("C"));
+        } };
+        checker = new DummyDependencyChecker(dependentItems);
+        resolver = new DependencyResolver<String>(checker);
+        final Collection<String> items = new ArrayList<String>() { {
+            for (DependentString dependentItem : dependentItems) {
+                add(dependentItem.theString);
+            }
+        } };
+        resolver.resolve(items);
+        assertEquals(3, checker.calls);
+        assertTrue("All the items should have been resolved.",
+            checker.resolved.containsAll(items));
+    }
+
+    /**
+     * Test the resolution of items which are all resolvable, but, handed in
+     * inverted order of their resolution:
+     * A -> {B, C}, B -> {C} and C -> {}
+     */
+    public void testResolveWithResolvableItemsButHandedInInvertedOrder() {
+        final Collection<DependentString> dependentItems =
+            new ArrayList<DependentString>() { {
+                add(new DependentString("A", new HashSet<String>()  { {
+                    add("B"); add("C"); } }));
+                add(new DependentString("B", new HashSet<String>() { {
+                    add("C"); } }));
+                add(new DependentString("C"));
+        } };
+        checker = new DummyDependencyChecker(dependentItems);
+        resolver = new DependencyResolver<String>(checker);
+        Collection<String> items = new ArrayList<String>() { {
+            for (DependentString dependentItem : dependentItems) {
+                add(dependentItem.theString);
+            }
+        } };
+        resolver.resolve(items);
+        assertTrue("All the items should have been resolved.",
+            checker.resolved.containsAll(items));
+    }
+
+    /**
+     * Test the resolution of items which are all resolvable, but, handed first
+     * partially and not resolvable, then a new call to resolve delivers the
+     * solution.
+     * First call: A -> {B, C}, B -> {C} and C -> {D}
+     * Second call: D -> {}
+     */
+    public void testResolveWithResolvableItemsButInTwoCalls() {
+        final Collection<DependentString> dependentItems =
+            new ArrayList<DependentString>() { {
+                add(new DependentString("C", new HashSet<String>() { {
+                    add("D"); } }));
+                add(new DependentString("D"));
+        } };
+        checker = new DummyDependencyChecker(dependentItems);
+        resolver = new DependencyResolver<String>(checker);
+        Collection<String> items1 = new ArrayList<String>() { { add("C"); } };
+        resolver.resolve(items1);
+        assertEquals("No item should have been resolved.", 0,
+            checker.resolved.size());
+        Collection<String> items2 = new ArrayList<String>() { { add("D"); } };
+        resolver.resolve(items2);
+        assertTrue("All the items should have been resolved.",
+            checker.resolved.containsAll(items1)
+            && checker.resolved.containsAll(items2));
+    }
+
+    /**
+     * Test the resolution of items which are not resolvable at first, but,
+     * which after injecting the dependency, will afterwards be resolved with
+     * the non-arguments resolve being invoked.
+     * <ol>
+     * <li>resolve(X -> {Z, Y}, Z -> {Y}) &rarr; nothing resolved</li>
+     * <li>{@link DependencyResolver#resolve()} call &rarr; nothing
+     * resolved</li>
+     * <li>inject Y as resolved</li>
+     * <li>{@link DependencyResolver#resolve()} call &rarr; X and Z
+     * resolved</li>
+     * </ol>
+     */
+    public void testResolveWithSolutionInjectedByThirdParty() {
+        final Collection<DependentString> dependentItems =
+            new ArrayList<DependentString>() { {
+                add(new DependentString("X", new HashSet<String>() { {
+                    add("Z"); add("Y"); } }));
+                add(new DependentString("Z", new HashSet<String>() { {
+                    add("Y"); } }));
+        } };
+        checker = new DummyDependencyChecker(dependentItems);
+        resolver = new DependencyResolver<String>(checker);
+        Collection<String> items = new ArrayList<String>() { { 
+            add("X"); add("Z"); } };
+        resolver.resolve(items);
+        assertEquals("No item should have been resolved.", 0,
+            checker.resolved.size());
+        resolver.resolve();
+        assertEquals("No item should have been resolved.", 0,
+            checker.resolved.size());
+        checker.resolved.add("Y");
+        resolver.resolve();
+        assertTrue("All the items should have been resolved.",
+            checker.resolved.containsAll(items));
+    }
+}
+
+class DependentString {
+    String theString;
+    Set<String> dependencies;
+
+    DependentString(String s, Set<String> dependencies) {
+        theString = s;
+        this.dependencies = dependencies;
+        if (dependencies == null) {
+            this.dependencies = new HashSet<String>();
+        }
+    }
+
+    DependentString(String s) {
+        this(s, new HashSet<String>());
+    }
+
+    /**
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        return theString.equals(obj);
+    }
+
+    /**
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return theString.hashCode();
+    }
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for (String dependency : dependencies) {
+            sb.append(dependency);
+            sb.append(", ");
+        }
+        return theString + " -> {" + sb.toString() + "}";
+    }
+}
+
+class DummyDependencyChecker implements DependencyChecker<String> {
+    Collection<String> items;
+    Collection<DependentString> dependentItems;
+    Collection<String> resolved = new HashSet<String>();
+    int calls = 0;
+
+    DummyDependencyChecker(final Collection<DependentString> items) {
+        this.items = new ArrayList<String>() { {
+            for (DependentString item : items) {
+                add(item.theString);
+            }
+        } };
+        this.dependentItems = items;
+    }
+
+    /**
+     * @see org.argouml.profile.internal.DependencyChecker#check(java.lang.Object)
+     */
+    public boolean check(String item) {
+        calls++;
+        if (items.contains(item)) {
+            DependentString item2Check = null;
+            for (DependentString theItem : dependentItems) {
+                if (theItem.equals(item)) {
+                    item2Check = theItem;
+                    break;
+                }
+            }
+            if (resolved.containsAll(item2Check.dependencies)) {
+                resolved.add(item2Check.theString);
+                return true;
+            }
+        }
+        return false;
+    }
+}

Modified: trunk/src/argouml-app/tests/org/argouml/profile/internal/TestProfileManagerImpl.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/tests/org/argouml/profile/internal/TestProfileManagerImpl.java?view=diff&pathrev=17979&r1=17978&r2=17979
==============================================================================
--- trunk/src/argouml-app/tests/org/argouml/profile/internal/TestProfileManagerImpl.java	(original)
+++ trunk/src/argouml-app/tests/org/argouml/profile/internal/TestProfileManagerImpl.java	2010-02-10 17:24:27-0800
@@ -1,6 +1,6 @@
 /* $Id$
  *****************************************************************************
- * Copyright (c) 2009 Contributors - see below
+ * Copyright (c) 2009-2010 Contributors - see below
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -8,6 +8,7 @@
  *
  * Contributors:
  *    thn
+ *    euluis
  *****************************************************************************
  *
  * Some portions of this file was previously release using the BSD License:
@@ -38,7 +39,15 @@
 
 package org.argouml.profile.internal;
 
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -46,15 +55,18 @@
 
 import junit.framework.TestCase;
 
+import org.argouml.FileHelper;
 import org.argouml.model.InitializeModel;
+import org.argouml.model.UmlException;
 import org.argouml.profile.Profile;
 import org.argouml.profile.ProfileException;
 import org.argouml.profile.ProfileManager;
+import org.argouml.profile.ProfileMother;
 import org.argouml.uml.cognitive.critics.ProfileGoodPractices;
 
 /**
  * Tests for the ProfileManagerImpl class.
- * 
+ *
  * @author Luis Sergio Oliveira (euluis)
  */
 public class TestProfileManagerImpl extends TestCase {
@@ -137,5 +149,111 @@
         assertFalse(manager.getRegisteredProfiles().contains(testProfile));
         assertFalse(manager.getDefaultProfiles().contains(testProfile));
     }
+    
+    /**
+     * When loading profiles, check that XMI profile dependency resolution is
+     * handled correctly when two user defined profiles are handed to
+     * {@link ProfileManagerImpl#loadProfiles(List)} with the first being the
+     * dependent profile of the second.
+     * 
+     * @throws IOException when file IO goes wrong...
+     * @throws UmlException when UML manipulation goes wrong...
+     */
+    public void testXmiProfileDependencyResolutionWithProfilesHandedInReverseOrderOfDependencyInOneCall()
+            throws IOException, UmlException {
+        List<Profile> registeredProfiles = manager.getRegisteredProfiles();
+        int numRegisteredBefore = registeredProfiles.size();
+        ProfileMother mother = new ProfileMother();
+        List<File> profileFiles =
+            mother.createProfileFilePairWithSecondDependingOnFirstThroughXmi();
+        Collections.reverse(profileFiles);
+        
+        ProfileManagerImpl managerImpl = (ProfileManagerImpl) manager;
+        managerImpl.loadProfiles(profileFiles);
+        
+        List<Profile> registeredProfilesAfter =
+            manager.getRegisteredProfiles();
+        assertEquals("Now we should have two more registered profiles.",
+            numRegisteredBefore + 2, registeredProfilesAfter.size());
+    }
+
+    /**
+     * When loading profiles, check that XMI profile dependency resolution is
+     * handled correctly when two user defined profiles are handed to
+     * {@link ProfileManagerImpl#loadProfiles(List)} in two calls, with the
+     * first call handing the dependent profile of the second and only on the
+     * second call being handed the profile from which the first depends.
+     * 
+     * @throws IOException when file IO goes wrong...
+     * @throws UmlException when UML manipulation goes wrong...
+     */
+    public void testXmiProfileDependencyResolutionWithProfilesHandedInReverseOrderOfDependencyInTwoCalls()
+            throws IOException, UmlException {
+        List<Profile> registeredProfiles = manager.getRegisteredProfiles();
+        int numRegisteredBefore = registeredProfiles.size();
+        ProfileMother mother = new ProfileMother();
+        List<File> profileFiles =
+            mother.createProfileFilePairWithSecondDependingOnFirstThroughXmi();
+        
+        File baseProfileFile = profileFiles.get(0);
+        String baseProfileFileName = baseProfileFile.getName();
+        File baseProfileDirectory = FileHelper.createTempDirectory(
+            getClass().getCanonicalName());
+        String newBaseProfileFileName = "new-base-profile.xmi";
+        File newBaseProfileFile = new File(baseProfileDirectory,
+            newBaseProfileFileName);
+        assertTrue(baseProfileFile.renameTo(newBaseProfileFile));
+        baseProfileFile = newBaseProfileFile;
+        
+        ProfileManagerImpl managerImpl = (ProfileManagerImpl) manager;
+        
+        File dependentProfileFile = profileFiles.get(1);
+        replaceStringInFile(dependentProfileFile, baseProfileFileName,
+            newBaseProfileFileName);
+        ArrayList<File> dependentProfileList = new ArrayList<File>();
+        dependentProfileList.add(dependentProfileFile);
+        managerImpl.loadProfiles(dependentProfileList);
+        assertEquals("We should have exaclty the same number of registered "
+            + "profiles as in the begining.",
+            numRegisteredBefore, manager.getRegisteredProfiles().size());
+        
+        ArrayList<File> baseProfileList = new ArrayList<File>();
+        baseProfileList.add(baseProfileFile);
+        managerImpl.loadProfiles(baseProfileList);
+        
+        assertEquals("Now we should have two more registered profiles.",
+            numRegisteredBefore + 2, manager.getRegisteredProfiles().size());
+    }
 
+    private void replaceStringInFile(File file, String regex,
+            String replacement) throws IOException {
+        StringBuffer fileContents = new StringBuffer();
+        BufferedReader reader = null;
+        String fileContents2 = null;
+        try {
+            reader = new BufferedReader(new FileReader(file));
+            String line = "";
+            while (null != (line = reader.readLine())) {
+                fileContents.append(line);
+                fileContents.append("\n");
+            }
+            fileContents2 = fileContents.toString();
+            fileContents2 = fileContents2.replaceAll(regex, replacement);
+        } finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+        if (fileContents2 != null && file.delete() && file.createNewFile()) {
+            BufferedWriter writer = null;
+            try {
+                writer = new BufferedWriter(new FileWriter(file));
+                writer.append(fileContents2);
+            } finally {
+                if (writer != null) {
+                    writer.close();
+                }
+            }
+        }
+    }
 }


------------------------------------------------------
http://argouml.tigris.org/ds/viewMessage.do?dsForumId=5905&dsMessageId=2446587

To unsubscribe from this discussion, e-mail: [[email protected]].
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.