/* $Id: ProfileManagerImpl.java 18321 2010-04-21 19:13:43Z bobtarling $ ***************************************************************************** * 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * euluis ***************************************************************************** * * Some portions of this file was previously release using the BSD License: */ // Copyright (c) 2007 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 // and this paragraph appear in all copies. This software program and // documentation are copyrighted by The Regents of the University of // California. The software program and documentation are supplied "AS // IS", without any accompanying services from The Regents. The Regents // does not warrant that the operation of the program will be // uninterrupted or error-free. The end-user understands that the program // was developed for research purposes and is advised not to rely // exclusively on the program for any reason. IN NO EVENT SHALL THE // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. package org.argouml.profile.internal; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import org.apache.log4j.Logger; import org.argouml.cognitive.Agency; import org.argouml.cognitive.Critic; import org.argouml.configuration.Configuration; import org.argouml.configuration.ConfigurationKey; import org.argouml.kernel.ProfileConfiguration; import org.argouml.model.Model; import org.argouml.model.UmlException; import org.argouml.profile.Profile; import org.argouml.profile.ProfileException; import org.argouml.profile.ProfileManager; import org.argouml.profile.UserDefinedProfile; import org.argouml.profile.UserDefinedProfileHelper; import org.argouml.uml.cognitive.critics.ProfileCodeGeneration; import org.argouml.uml.cognitive.critics.ProfileGoodPractices; /** * Default <code>ProfileManager</code> implementation * * @author Marcos Aurelio */ public class ProfileManagerImpl implements ProfileManager { private static final Logger LOG = Logger.getLogger( ProfileManagerImpl.class); private static final String DIRECTORY_SEPARATOR = "*"; /** * The configuration key for the default profiles. */ public static final ConfigurationKey KEY_DEFAULT_PROFILES = Configuration .makeKey("profiles", "default"); /** * The configuration key for the search directories. */ public static final ConfigurationKey KEY_DEFAULT_DIRECTORIES = Configuration .makeKey("profiles", "directories"); /** * Avoids recursive configuration update when loading configuration */ private boolean disableConfigurationUpdate = false; private List<Profile> profiles = new ArrayList<Profile>(); private List<Profile> defaultProfiles = new ArrayList<Profile>(); private List<String> searchDirectories = new ArrayList<String>(); private ProfileUML profileUML; private ProfileGoodPractices profileGoodPractices; private ProfileCodeGeneration profileCodeGeneration; private DependencyResolver<File> resolver; /** * Constructor - includes initialization of built-in default profiles. */ public ProfileManagerImpl() { try { disableConfigurationUpdate = true; profileUML = new ProfileUML(); profileGoodPractices = new ProfileGoodPractices(); profileCodeGeneration = new ProfileCodeGeneration( profileGoodPractices); registerProfileInternal(profileUML); addToDefaultProfiles(profileUML); // the UML Profile is always present and default // register the built-in profiles registerProfileInternal(profileGoodPractices); registerProfileInternal(profileCodeGeneration); registerProfileInternal(new ProfileMeta()); } catch (ProfileException e) { // TODO: Why is this throwing a generic runtime exception?!?! throw new RuntimeException(e); } 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; String defaultProfilesList = Configuration .getString(KEY_DEFAULT_PROFILES); if (defaultProfilesList.equals("")) { // 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); while (tokenizer.hasMoreTokens()) { String desc = tokenizer.nextToken(); Profile p = null; if (desc.charAt(0) == 'U') { String fileName = desc.substring(1); File file; try { file = new File(new URI(fileName)); p = findUserDefinedProfile(file); if (p == null) { try { p = new UserDefinedProfile(file, this); registerProfileInternal(p); } catch (ProfileException e) { LOG.error("Error loading profile: " + file, e); } } } catch (URISyntaxException e1) { LOG.error("Invalid path for Profile: " + fileName, e1); } catch (Throwable e2) { LOG.error("Error loading profile: " + fileName, e2); } } else if (desc.charAt(0) == 'C') { String profileIdentifier = desc.substring(1); p = lookForRegisteredProfile(profileIdentifier); } if (p != null) { addToDefaultProfiles(p); } } } disableConfigurationUpdate = false; } } private void updateDefaultProfilesConfiguration() { if (!disableConfigurationUpdate) { StringBuffer buf = new StringBuffer(); for (Profile p : defaultProfiles) { if (p instanceof UserDefinedProfile) { buf.append("U" + ((UserDefinedProfile) p).getModelFile() .toURI().toASCIIString()); } 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) { 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) { try { 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); } } } return loadDefaultProfilesFromConfiguration; } catch (RuntimeException e) { // TODO: Better if we wrap in a ProfileException and throw that LOG.error("Error registering profile " + p.getDisplayName()); throw e; } } public void removeProfile(Profile p) { if (p != null && p != profileUML) { profiles.remove(p); defaultProfiles.remove(p); } try { Collection packages = p.getLoadedPackages(); if (packages != null && !packages.isEmpty()) { // We assume profile is contained in a single extent Model.getUmlFactory().deleteExtent(packages.iterator().next()); } } catch (ProfileException e) { // Nothing to delete if we couldn't get the packages } } private static final String OLD_PROFILE_PACKAGE = "org.argouml.uml.profile"; private static final String NEW_PROFILE_PACKAGE = "org.argouml.profile.internal"; public Profile getProfileForClass(String profileClass) { Profile found = null; // If we found an old-style name, update it to the new package name if (profileClass != null && profileClass.startsWith(OLD_PROFILE_PACKAGE)) { profileClass = profileClass.replace(OLD_PROFILE_PACKAGE, NEW_PROFILE_PACKAGE); } // Make sure the names didn't change again assert profileUML.getClass().getName().startsWith(NEW_PROFILE_PACKAGE); for (Profile p : profiles) { if (p.getClass().getName().equals(profileClass)) { found = p; break; } } return found; } public void addToDefaultProfiles(Profile p) { if (p != null && profiles.contains(p) && !defaultProfiles.contains(p)) { defaultProfiles.add(p); updateDefaultProfilesConfiguration(); } } public List<Profile> getDefaultProfiles() { return Collections.unmodifiableList(defaultProfiles); } public void removeFromDefaultProfiles(Profile p) { if (p != null && p != profileUML && profiles.contains(p)) { defaultProfiles.remove(p); updateDefaultProfilesConfiguration(); } } public void addSearchPathDirectory(String path) { if (path != null && !searchDirectories.contains(path)) { searchDirectories.add(path); updateSearchDirectoriesConfiguration(); try { Model.getXmiReader().addSearchPath(path); } catch (UmlException e) { LOG.error("Couldn't retrive XMI Reader from Model.", e); } } } public List<String> getSearchPathDirectories() { return Collections.unmodifiableList(searchDirectories); } public void removeSearchPathDirectory(String path) { if (path != null) { searchDirectories.remove(path); updateSearchDirectoriesConfiguration(); try { Model.getXmiReader().removeSearchPath(path); } catch (UmlException e) { LOG.error("Couldn't retrive XMI Reader from Model.", e); } } } 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? 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; } } } return null; } public Profile getUMLProfile() { return profileUML; } /* * @see org.argouml.profile.ProfileManager#lookForRegisteredProfile(java.lang.String) */ public Profile lookForRegisteredProfile(String value) { if (value != null) { List<Profile> registeredProfiles = getRegisteredProfiles(); for (Profile profile : registeredProfiles) { if (value.equalsIgnoreCase(profile.getProfileIdentifier())) { return profile; } } } return null; } /* * @param pc * @see org.argouml.profile.ProfileManager#applyConfiguration(org.argouml.kernel.ProfileConfiguration) */ public void applyConfiguration(ProfileConfiguration pc) { for (Profile p : this.profiles) { for (Critic c : p.getCritics()) { c.setEnabled(false); Configuration.setBoolean(c.getCriticKey(), false); } } for (Profile p : pc.getProfiles()) { for (Critic c : p.getCritics()) { c.setEnabled(true); Configuration.setBoolean(c.getCriticKey(), true); } } } }