svn commit: r16688 - trunk/src/argouml-app: src/org/argouml/persistence tests/org/argouml/persistence

Tom Morris <[email protected]>
Newsgroups gmane.comp.lang.uml.argouml.cvs
Message-ID <[email protected]>
Author: tfmorris
Date: 2009-01-23 13:11:04-0800
New Revision: 16688

Modified:
   trunk/src/argouml-app/src/org/argouml/persistence/ProfileConfigurationFilePersister.java
   trunk/src/argouml-app/tests/org/argouml/persistence/TestProfileConfigurationFilePersister.java

Log:
RESOLVED - issue 5656: Switch profile parsing to SAXParser based implementation.  Add tests.
http://argouml.tigris.org/issues/show_bug.cgi?id=5656

Modified: trunk/src/argouml-app/src/org/argouml/persistence/ProfileConfigurationFilePersister.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/src/org/argouml/persistence/ProfileConfigurationFilePersister.java?view=diff&pathrev=16688&r1=16687&r2=16688
==============================================================================
--- trunk/src/argouml-app/src/org/argouml/persistence/ProfileConfigurationFilePersister.java	(original)
+++ trunk/src/argouml-app/src/org/argouml/persistence/ProfileConfigurationFilePersister.java	2009-01-23 13:11:04-0800
@@ -1,5 +1,5 @@
 // $Id$
-// Copyright (c) 2007-2008 The Regents of the University of California. All
+// Copyright (c) 2007-2009 The Regents of the University of California. All
 // Rights Reserved. Permission to use, copy, modify, and distribute this
 // software and its documentation without fee, and without a written
 // agreement is hereby granted, provided that the above copyright notice
@@ -24,12 +24,10 @@
 
 package org.argouml.persistence;
 
-import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
@@ -38,7 +36,6 @@
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Iterator;
 import java.util.List;
 
 import org.apache.log4j.Logger;
@@ -55,6 +52,8 @@
 import org.argouml.profile.ProfileFacade;
 import org.argouml.profile.ProfileManager;
 import org.argouml.profile.UserDefinedProfile;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
 
 /**
  * Persister for project's profile configuration.
@@ -79,39 +78,16 @@
     public void load(Project project, InputStream inputStream)
         throws OpenException {
         try {
-            BufferedReader br = new BufferedReader(new InputStreamReader(
-                    inputStream, Argo.getEncoding()));
-
-            String line = null;
-            while (true) {
-                line = br.readLine();
-                if (line.trim().equals("<profile>")) {
-                    break;
-                }
+            ProfileConfigurationParser parser = 
+                new ProfileConfigurationParser();
+            parser.parse(new InputSource(inputStream));
+            Collection<Profile> profiles = parser.getProfiles();
+
+            Collection<String> unresolved = parser.getUnresolvedFilenames();
+            if (!unresolved.isEmpty()) {
+                profiles.addAll(loadUnresolved(unresolved));
             }
-            Collection<Profile> profiles = new ArrayList<Profile>();
-            while (true) {
-                line = br.readLine().trim();
-                if (line.equals("</profile>")) {
-                    break;
-                }
-                
-                Profile profile = null;
 
-                if (line.equals("<userDefined>")) {
-                    profile = handleUserDefinedProfile(br);
-                    // consumes the </userDefined>
-                    br.readLine();
-                } else if (line.equals("<plugin>")) {
-                    profile = handlePluginProfile(br);
-                    // consumes closing tag
-                    br.readLine();
-                }
-
-                if (profile != null) {
-                    profiles.add(profile);
-                }
-            }
             ProfileConfiguration pc = new ProfileConfiguration(project, 
                     profiles);
             project.setProfileConfiguration(pc);
@@ -123,65 +99,28 @@
         }
     }
 
-    private static Profile handlePluginProfile(BufferedReader br)
-        throws IOException, OpenException {
-        Profile profile;
-        String profileIdentifier = br.readLine().trim();
-        profile = ProfileFacade.getManager().lookForRegisteredProfile(
-                profileIdentifier);
-        if (profile == null) {
-            
-            // for compatibility with older format
-            profile = ProfileFacade.getManager().getProfileForClass(
-                    profileIdentifier);
-            
-            if (profile == null) {
-                throw new OpenException("Plugin profile \"" + profileIdentifier
-                        + "\" is not available in installation.", null);
-            }
-        }
-        return profile;
-    }
-
-    private static Profile handleUserDefinedProfile(BufferedReader br)
-        throws IOException, OpenException {
-        String line;
-        Profile profile;
-        line = br.readLine().trim();
-        String fileName = line.substring(line.indexOf(">") + 1,
-                line.indexOf("</")).trim();
-
-        // consumes the <model> tag
-        br.readLine();
 
-        StringBuffer xmi = new StringBuffer();
-        
-        while (true) {
-            line = br.readLine();
-            if (line == null || line.contains("</model>")) {
-                break;
-            }
-            xmi.append(line + "\n");
-        }
+    /**
+     * Use XMI as a fall back alternative when the file for the user defined
+     * profile isn't found by the profile manager.
+     * <p>
+     * TODO: work in progress, see issue 5039
+     * 
+     * @param unresolved collection of unresolved filenames from the parser
+     * @return collection of resolved profiles
+     */
+    private Collection<Profile> loadUnresolved(Collection<String> unresolved) {
+        Collection<Profile> profiles = new ArrayList<Profile>();        
         ProfileManager profileManager = ProfileFacade.getManager();
-        profile = getMatchingUserDefinedProfile(fileName, 
-            profileManager);
-        if (profile == null) {
-            throw new OpenException(
-                "User defined profile \"" + fileName 
-                + "\" isn't available in the current configuration.",
-                    null);
-            // Use XMI as a fall back alternative when the 
-            // file for the user defined profile isn't found by the 
-            // profile manager.
+        for (String filename : unresolved) {
             // TODO: work in progress, see issue 5039
-//                        addUserDefinedProfile(fileName, xmi, profileManager);
-//                        profile = getMatchingUserDefinedProfile(fileName, 
-//                            profileManager);
-//                        assert profile != null 
-//                            : "Profile should have been found now.";
+//            addUserDefinedProfile(filename, xmi, profileManager);
+//            Profile profile = getMatchingUserDefinedProfile(filename,
+//                    profileManager);
+//            assert profile != null : "Profile should have been found now.";
+//            profiles.add(profile);
         }
-        return profile;
+        return profiles;
     }
 
     /**
@@ -216,21 +155,6 @@
         }
     }
 
-    private static Profile getMatchingUserDefinedProfile(String fileName, 
-            ProfileManager profileManager) {
-        for (Profile candidateProfile 
-            : profileManager.getRegisteredProfiles()) {
-            if (candidateProfile instanceof UserDefinedProfile) {
-                UserDefinedProfile userProfile = 
-                    (UserDefinedProfile) candidateProfile;
-                if (userProfile.getModelFile() != null
-                    && fileName.equals(userProfile.getModelFile().getName())) {
-                    return userProfile;
-                }
-            }
-        }
-        return null;
-    }
 
     private static File getProfilesDirectory(ProfileManager profileManager) {
         if (isSomeProfileDirectoryConfigured(profileManager)) {
@@ -275,13 +199,12 @@
 		ProfileConfiguration pc = (ProfileConfiguration) member;
 
 		w.println("<?xml version = \"1.0\" encoding = \"UTF-8\" ?>");
-		w.println("<!DOCTYPE profile SYSTEM \"profile.dtd\" >");
+		// TODO: This DTD doesn't exist, so we can't tell readers to
+		// look for it
+//		w.println("<!DOCTYPE profile SYSTEM \"profile.dtd\" >");
 		w.println("<profile>");
 
-		Iterator it = pc.getProfiles().iterator();
-		while (it.hasNext()) {
-                    Profile profile = (Profile) it.next();
-
+		for (Profile profile : pc.getProfiles()) {
                     if (profile instanceof UserDefinedProfile) {
                         UserDefinedProfile uprofile = 
                             (UserDefinedProfile) profile;
@@ -340,5 +263,237 @@
             throw new OpenException(e);
         }
     }
-
+    
 }
+
+/**
+ * Parser for Profile Configuration.
+ * 
+ * @author Tom Morris <[email protected]>
+ */
+class ProfileConfigurationParser extends SAXParserBase {
+
+    private static final Logger LOG = Logger
+            .getLogger(ProfileConfigurationParser.class);
+
+    private ProfileConfigurationTokenTable tokens = 
+        new ProfileConfigurationTokenTable();
+
+    private Profile profile;
+
+    private String model;
+
+    private String filename;
+
+    private Collection<Profile> profiles = new ArrayList<Profile>();
+
+    private Collection<String> unresolvedFilenames = new ArrayList<String>();
+
+    /**
+     * Construct the parser.
+     */
+    public ProfileConfigurationParser() {
+        // Empty constructor
+    }
+
+    public Collection<Profile> getProfiles() {
+        return profiles;
+    }
+
+    public Collection<String> getUnresolvedFilenames() {
+        return unresolvedFilenames;
+    }
+
+    public void handleStartElement(XMLElement e) {
+
+        try {
+            switch (tokens.toToken(e.getName(), true)) {
+
+            case ProfileConfigurationTokenTable.TOKEN_PROFILE:
+                break;
+            case ProfileConfigurationTokenTable.TOKEN_PLUGIN:
+                profile = null;
+                break;
+            case ProfileConfigurationTokenTable.TOKEN_USER_DEFINED:
+                profile = null;
+                filename = null;
+                model = null;
+                break;
+            case ProfileConfigurationTokenTable.TOKEN_FILENAME:
+                break;
+            case ProfileConfigurationTokenTable.TOKEN_MODEL:
+                break;
+
+            default:
+                LOG.warn("WARNING: unknown tag:" + e.getName());
+                break;
+            }
+        } catch (Exception ex) {
+            LOG.error("Exception in startelement", ex);
+        }
+    }
+
+    /**
+     * Called by the XML implementation to signal the end of an XML entity.
+     * 
+     * @param e The XML entity that ends.
+     * @throws SAXException on any error
+     */
+    public void handleEndElement(XMLElement e) throws SAXException {
+
+        try {
+            switch (tokens.toToken(e.getName(), false)) {
+
+            case ProfileConfigurationTokenTable.TOKEN_PROFILE:
+                handleProfileEnd(e);
+                break;
+            case ProfileConfigurationTokenTable.TOKEN_PLUGIN:
+                handlePluginEnd(e);
+                break;
+            case ProfileConfigurationTokenTable.TOKEN_USER_DEFINED:
+                handleUserDefinedEnd(e);
+                break;
+            case ProfileConfigurationTokenTable.TOKEN_FILENAME:
+                handleFilenameEnd(e);
+                break;
+            case ProfileConfigurationTokenTable.TOKEN_MODEL:
+                handleModelEnd(e);
+                break;
+
+            default:
+                LOG.warn("WARNING: unknown end tag:" + e.getName());
+                break;
+            }
+        } catch (Exception ex) {
+            throw new SAXException(ex);
+        }
+    }
+
+    protected void handleProfileEnd(XMLElement e) {
+        if (profiles.isEmpty()) {
+            LOG.warn("No profiles defined");
+        }
+    }
+
+    protected void handlePluginEnd(XMLElement e) throws SAXException {
+        String name = e.getText().trim();
+        profile = lookupProfile(name);
+        if (profile != null) {
+            profiles.add(profile);
+            LOG.debug("Found plugin profile " + name);
+        } else {
+            LOG.error("Unabled to find plugin profile - " + name);
+        }
+    }
+
+    private static Profile lookupProfile(String profileIdentifier)
+        throws SAXException {
+        Profile profile;
+        profile = ProfileFacade.getManager().lookForRegisteredProfile(
+                profileIdentifier);
+        if (profile == null) {
+
+            // for compatibility with older format
+            profile = ProfileFacade.getManager().getProfileForClass(
+                    profileIdentifier);
+
+            if (profile == null) {
+                throw new SAXException("Plugin profile \"" + profileIdentifier
+                        + "\" is not available in installation.", null);
+            }
+        }
+        return profile;
+    }
+
+    protected void handleUserDefinedEnd(XMLElement e) {
+        // <model> is not used in current implementation
+        if (filename == null /* || model == null */) {
+            LOG.error("Got badly formed user defined profile entry " + e);
+        }
+        profile = getMatchingUserDefinedProfile(filename, ProfileFacade
+                .getManager());
+
+        if (profile == null) {
+            unresolvedFilenames.add(filename);
+        } else {
+            profiles.add(profile);
+            LOG.debug("Loaded user defined profile - filename = " + filename);
+        }
+
+    }
+
+    private static Profile getMatchingUserDefinedProfile(String fileName,
+            ProfileManager profileManager) {
+        for (Profile candidateProfile 
+                : profileManager.getRegisteredProfiles()) {
+            if (candidateProfile instanceof UserDefinedProfile) {
+                UserDefinedProfile userProfile = 
+                    (UserDefinedProfile) candidateProfile;
+                if (userProfile.getModelFile() != null
+                        && fileName
+                                .equals(userProfile.getModelFile().getName())) {
+                    return userProfile;
+                }
+            }
+        }
+        return null;
+    }
+
+    protected void handleFilenameEnd(XMLElement e) {
+        filename = e.getText().trim();
+        LOG.debug("Got filename = " + filename);
+    }
+
+    protected void handleModelEnd(XMLElement e) {
+        model = e.getText().trim();
+        LOG.debug("Got model = " + model);
+    }
+
+    /**
+     * Token Table for Profile Configuration parser.
+     * 
+     * @author Tom Morris
+     */
+    class ProfileConfigurationTokenTable extends XMLTokenTableBase {
+
+        private static final String STRING_PROFILE = "profile";
+
+        private static final String STRING_PLUGIN = "plugin";
+
+        private static final String STRING_USER_DEFINED = "userDefined";
+
+        private static final String STRING_FILENAME = "filename";
+
+        private static final String STRING_MODEL = "model";
+
+        public static final int TOKEN_PROFILE = 1;
+
+        public static final int TOKEN_PLUGIN = 2;
+
+        public static final int TOKEN_USER_DEFINED = 3;
+
+        public static final int TOKEN_FILENAME = 4;
+
+        public static final int TOKEN_MODEL = 5;
+
+        private static final int TOKEN_LAST = 5;
+
+        public static final int TOKEN_UNDEFINED = 999;
+
+        /**
+         * Construct the token table.,
+         */
+        public ProfileConfigurationTokenTable() {
+            super(TOKEN_LAST);
+        }
+
+        protected void setupTokens() {
+            addToken(STRING_PROFILE, Integer.valueOf(TOKEN_PROFILE));
+            addToken(STRING_PLUGIN, Integer.valueOf(TOKEN_PLUGIN));
+            addToken(STRING_USER_DEFINED, Integer.valueOf(TOKEN_USER_DEFINED));
+            addToken(STRING_FILENAME, Integer.valueOf(TOKEN_FILENAME));
+            addToken(STRING_MODEL, Integer.valueOf(TOKEN_MODEL));
+        }
+    }
+
+}
\ No newline at end of file

Modified: trunk/src/argouml-app/tests/org/argouml/persistence/TestProfileConfigurationFilePersister.java
Url: http://argouml.tigris.org/source/browse/argouml/trunk/src/argouml-app/tests/org/argouml/persistence/TestProfileConfigurationFilePersister.java?view=diff&pathrev=16688&r1=16687&r2=16688
==============================================================================
--- trunk/src/argouml-app/tests/org/argouml/persistence/TestProfileConfigurationFilePersister.java	(original)
+++ trunk/src/argouml-app/tests/org/argouml/persistence/TestProfileConfigurationFilePersister.java	2009-01-23 13:11:04-0800
@@ -24,24 +24,41 @@
 
 package org.argouml.persistence;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Iterator;
 
 import junit.framework.TestCase;
 
+import org.argouml.application.api.Argo;
+import org.argouml.kernel.ProfileConfiguration;
+import org.argouml.kernel.Project;
+import org.argouml.kernel.ProjectManager;
 import org.argouml.model.InitializeModel;
 import org.argouml.model.Model;
 import org.argouml.model.UmlException;
 import org.argouml.model.XmiWriter;
 import org.argouml.profile.CoreProfileReference;
 import org.argouml.profile.FileModelLoader;
+import org.argouml.profile.Profile;
 import org.argouml.profile.ProfileException;
 import org.argouml.profile.ProfileModelLoader;
 import org.argouml.profile.ResourceModelLoader;
 import org.argouml.profile.UserProfileReference;
+import org.argouml.profile.init.InitProfileSubsystem;
+import org.argouml.profile.internal.ProfileJava;
+import org.argouml.profile.internal.ProfileUML;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
 
 /**
  * Tests for the {@link ProfileConfigurationFilePersister} class.
@@ -54,10 +71,11 @@
     protected void setUp() throws Exception {
         super.setUp();
         InitializeModel.initializeDefault();
+        new InitProfileSubsystem().init();
     }
 
     /**
-     * Demonstrates that XmiWriterMDRImpl fails to write a profile 
+     * Tests whether XmiWriterMDRImpl fails to write a profile 
      * (i.e., the file will contain no model) previously loaded from a file.
      * 
      * @throws ProfileException if the loading of the profile fails.
@@ -90,4 +108,91 @@
         assertEquals(umlModelName, Model.getFacade().getName(umlModel));
     }
 
+    private static final String TEST_PROFILE = 
+        "<?xml version = \"1.0\" encoding = \"UTF-8\" ?>\n"
+        // Although we've historically written out the DOCTYPE, the DTD doesn't
+        // actually exist and this line will get stripped by the .uml file
+        // persister
+//        + "<!DOCTYPE profile SYSTEM \"profile.dtd\" >\n"
+        + "<profile>\n"
+
+        // Standard UML 1.4 profile
+        + "\t\t<plugin>\n"
+        + "\t\t\tUML 1.4\n"
+        + "\t\t</plugin>\n"
+        
+        // Standard Java profile
+        + "\t\t<plugin>\n"
+        + "\t\t\tJava\n"
+        + "\t\t</plugin>\n"
+
+
+        // TODO: User defined profile support untested currently
+//        + "\t\t<userDefined>\n"
+//        + "\t\t\t<filename>\n"
+//        + "foo.profile\n"
+//        + "</filename>\n"
+//        + "\t\t\t<model>\n"
+//        + "foo.profile.package\n"
+//        + "\t\t\t</model>\n"
+//        + "\t\t</userDefined>\n"
+        
+        + "</profile>";
+        
+    /**
+     * Test the basic profile configuration parser.
+     * 
+     * @throws SAXException on a parse failure
+     * @throws UnsupportedEncodingException if our default encoding (UTF-8) is
+     *             unsupported. Should never happen.
+     */
+    public void testProfileConfigurationParser() throws SAXException,
+        UnsupportedEncodingException {
+        InputStream inStream = 
+            new ByteArrayInputStream(
+                    TEST_PROFILE.getBytes(Argo.getEncoding()));
+        ProfileConfigurationParser parser = new ProfileConfigurationParser();
+        parser.parse(new InputSource(inStream));
+        Collection<Profile> profiles = parser.getProfiles();
+        assertEquals("Wrong number of profiles", 2, profiles.size());
+        Iterator<Profile> profileIter = profiles.iterator();
+        assertTrue("Didn't get expected UML profile", 
+                profileIter.next() instanceof ProfileUML);
+        assertTrue("Didn't get expected Java profile", 
+                profileIter.next() instanceof ProfileJava);
+    }
+    
+    /**
+     * Test that we can save and restore the default profile configuration.
+     * 
+     * @throws IOException on io error
+     * @throws SaveException on save error
+     * @throws OpenException on load error
+     */
+    public void testSaveLoadDefaultConfiguration() throws IOException,
+        SaveException, OpenException {
+        
+        // Create a default profile and record its contents
+        Project project = ProjectManager.getManager().makeEmptyProject();
+        ProfileConfiguration pc = new ProfileConfiguration(project);
+        Collection<Profile> startingProfiles = 
+            new ArrayList<Profile>(pc.getProfiles());
+
+        // Write the profile out to a temp file
+        ProfileConfigurationFilePersister persister = 
+            new ProfileConfigurationFilePersister();
+        File file = File.createTempFile(this.getName(), ".profile");
+        OutputStream outStream = new FileOutputStream(file);
+        persister.save(pc, outStream);
+        outStream.close();
+        
+        // Read it back in to a new empty project
+        InputStream inStream = new FileInputStream(file);
+        project = ProjectManager.getManager().makeEmptyProject();
+        persister.load(project, inStream);
+ 
+        // Make sure we got what we started with
+        assertEquals(startingProfiles, 
+                project.getProfileConfiguration().getProfiles());
+    }
 }

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

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.