/* This file is part of Green. * * Copyright (C) 2005 The Research Foundation of State University of New York * All Rights Under Copyright Reserved, The Research Foundation of S.U.N.Y. * * Green is free software, licensed under the terms of the Eclipse * Public License, version 1.0. The license is available at * http://www.eclipse.org/legal/epl-v10.html */ package edu.buffalo.cse.green; import static edu.buffalo.cse.green.GreenException.GRERR_DUPLICATE_EXTENSION; import static edu.buffalo.cse.green.GreenException.GRERR_INVALID_CONTEXT_ACTION; import static edu.buffalo.cse.green.preferences.PreferenceInitializer.P_FILTERS_MEMBER; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.help.IWorkbenchHelpSystem; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import edu.buffalo.cse.green.constants.PluginConstants; import edu.buffalo.cse.green.designpattern.DesignPatternGroup; import edu.buffalo.cse.green.editor.DiagramEditor; import edu.buffalo.cse.green.editor.action.AlterRelationshipVisibilityAction; import edu.buffalo.cse.green.editor.action.ContextAction; import edu.buffalo.cse.green.editor.action.IncrementalExploreSingleAction; import edu.buffalo.cse.green.editor.controller.FieldPart; import edu.buffalo.cse.green.editor.controller.MethodPart; import edu.buffalo.cse.green.editor.controller.NotePart; import edu.buffalo.cse.green.editor.controller.TypePart; import edu.buffalo.cse.green.editor.model.MemberModel; import edu.buffalo.cse.green.editor.model.filters.MemberFilter; import edu.buffalo.cse.green.editor.save.ISaveFormat; import edu.buffalo.cse.green.relationships.RelationshipGenerator; import edu.buffalo.cse.green.relationships.RelationshipGroup; import edu.buffalo.cse.green.relationships.RelationshipRecognizer; import edu.buffalo.cse.green.relationships.RelationshipRemover; import edu.buffalo.cse.green.relationships.RelationshipSubtype; import edu.buffalo.cse.green.types.ITypeProperties; /** * The main plugin class to be used in the desktop. This class loads in all * extensions that are relevant to our editor. * * @author bcmartin * */ public final class PlugIn extends AbstractUIPlugin { private static PlugIn PLUGIN; private static ResourceBundle BUNDLE; /** * The collection of design pattern plugins in Green. * <em>This is not yet implemented.</em> */ private static List<DesignPatternGroup> _designPatterns = new ArrayList<DesignPatternGroup>(); private static Map<Class, Class> _partToView = new HashMap<Class, Class>(); private static Map<Class, RelationshipGroup> _relationshipMap = new HashMap<Class, RelationshipGroup>(); private static List<ContextAction> _actions = new ArrayList<ContextAction>(); private static final String resourceBundleId = "edu.buffalo.cse.green.PlugInPluginResources"; private static final String DESIGN_PATTERN_MENU_NAME = "Design Patterns"; private static final String CONTEXT_ACTION_ID = "edu.buffalo.cse.green.contextAction"; private static final String DESIGN_PATTERN_ID = "edu.buffalo.cse.green.designPattern"; private static final String RELATIONSHIP_ID = "edu.buffalo.cse.green.relationships"; private static final String VIEW_ID = "edu.buffalo.cse.green.editorViews"; private static final String SAVE_FORMAT_ID = "edu.buffalo.cse.green.saveFormat"; private static final String JAVA_TYPE_ID = "edu.buffalo.cse.green.javaType"; private static boolean _recognizersEnabled = true; private static boolean _isUserMode = true; private static Map<String, List<RelationshipSubtype>> _relationships; private static Map<String, ISaveFormat> SAVE_FORMAT_MAP = new HashMap<String, ISaveFormat>(); private static Map<String, ITypeProperties> _mTypeProperties = new HashMap<String, ITypeProperties>(); private static List<RelationshipGroup> _relationshipGroups; public PlugIn() { _relationships = new HashMap<String, List<RelationshipSubtype>>(); _relationshipGroups = new ArrayList<RelationshipGroup>(); PLUGIN = this; try { BUNDLE = ResourceBundle.getBundle(resourceBundleId); } catch (MissingResourceException e) { BUNDLE = null; } } /** * Prevents dialogs that would obstruct tests from appearing */ public static void setTestMode() { _isUserMode = false; } /** * @return The value of whether Green is being run in user mode or test * mode. In test mode, error dialogs do not appear and cardinalites are set * before relationships are drawn. */ public static boolean isUserMode() { return _isUserMode; } /** * @return The <code>IWorkbench</code>'s help system. */ public static IWorkbenchHelpSystem getWorkbenchHelp() { return PlugIn.getDefault().getWorkbench().getHelpSystem(); } /** * @param klass - * The Class of the relationship's model * @return the relationship group corresponding to the given name. */ public static RelationshipGroup getRelationshipGroup(Class klass) { return (RelationshipGroup) _relationshipMap.get(klass); } /** * Adds a <code>RelationshipGroup</code> to Green's relationship types. * * @param group - The <code>RelationshipGroup</code> to add. */ private static void addRelationshipGroup(RelationshipGroup group) { // map the relationship's part to the group _relationshipMap.put(group.getPartClass(), group); _relationshipGroups.add(group); List<RelationshipSubtype> rels = _relationships.get(group.getName()); RelationshipSubtype relSubtype = new RelationshipSubtype(group, group.getSubtype()); if (rels == null) { rels = new ArrayList<RelationshipSubtype>(); rels.add(relSubtype); _relationships.put(group.getName(), rels); } else { rels.add(relSubtype); } } /** * @param name - The relationship's name. * @return A list of all the relationship subtypes of the given * relationship. */ public static List<RelationshipSubtype> getRelationshipSubtypes( String name) { return _relationships.get(name); } /** * @param extensionPointId - The given id. * @return A list of <code>IConfigurationElement</code>s corresponding to * the given extension point id. */ public List<IConfigurationElement> getConfigElements(String extensionPointId) { List<IConfigurationElement> configurationElements = new ArrayList<IConfigurationElement>(); IExtensionRegistry registry = Platform.getExtensionRegistry(); IExtension[] extensionPoints = registry.getExtensionPoint( extensionPointId).getExtensions(); for (IExtension extension : extensionPoints) { for (IConfigurationElement element : extension .getConfigurationElements()) { configurationElements.add(element); } } return configurationElements; } /** * This method is called upon plug-in activation */ public void start(BundleContext context) throws Exception { super.start(context); try { // load plugins: save types for (IConfigurationElement element : getConfigElements(SAVE_FORMAT_ID)) { ISaveFormat format = (ISaveFormat) element.createExecutableExtension("class"); if (SAVE_FORMAT_MAP.containsKey(format.getExtension())) { GreenException.illegalOperation(GRERR_DUPLICATE_EXTENSION); } String ext = format.getExtension(); Pattern valid = Pattern.compile("[a-z0-9]+"); Matcher matcher = valid.matcher(ext); if (!matcher.matches() || ext.length() > 4) { GreenException.illegalOperation( GreenException.GRERR_INVALID_EXTENSION); } SAVE_FORMAT_MAP.put(ext, format); } // load plugins: editor view plugin for (IConfigurationElement element : getConfigElements(VIEW_ID)) { // add new view String memberFigure = element.getAttribute("memberClass"); String noteFigure = element.getAttribute("noteClass"); String typeFigure = element.getAttribute("typeClass"); _partToView.put(FieldPart.class, Class.forName(memberFigure)); _partToView.put(MethodPart.class, Class.forName(memberFigure)); _partToView.put(NotePart.class, Class.forName(noteFigure)); _partToView.put(TypePart.class, Class.forName(typeFigure)); } // load plugins: context actions for (IConfigurationElement element : getConfigElements(CONTEXT_ACTION_ID)) { Object action = element.createExecutableExtension("class"); if (!(action instanceof ContextAction)) { GreenException.illegalOperation( GRERR_INVALID_CONTEXT_ACTION); } // Get the list that the action belongs to and add it. ContextAction contextAction = (ContextAction) action; _actions.add(contextAction); } // load plugins: relationships List<IConfigurationElement> elements = getConfigElements(RELATIONSHIP_ID); for (int x = 0; x < elements.size(); x += 5) { RelationshipGenerator gen = (RelationshipGenerator) elements.get(x + 1).createExecutableExtension("class"); RelationshipRecognizer rec = (RelationshipRecognizer) elements.get(x + 2).createExecutableExtension("class"); RelationshipRemover rem = (RelationshipRemover) elements.get(x + 3).createExecutableExtension("class"); boolean classToClass = !(elements.get( x + 4).getAttribute("classToClass").equals("")); boolean classToEnum = !(elements.get( x + 4).getAttribute("classToEnum").equals("")); boolean classToInterface = !(elements.get( x + 4).getAttribute("classToInterface").equals("")); boolean enumToClass = !(elements.get( x + 4).getAttribute("enumToClass").equals("")); boolean enumToEnum = !(elements.get( x + 4).getAttribute("enumToEnum").equals("")); boolean enumToInterface = !(elements.get( x + 4).getAttribute("enumToInterface").equals("")); boolean interfaceToClass = !(elements.get( x + 4).getAttribute("interfaceToClass").equals("")); boolean interfaceToEnum = !(elements.get( x + 4).getAttribute("interfaceToEnum").equals("")); boolean interfaceToInterface = !(elements.get( x + 4).getAttribute("interfaceToInterface").equals("")); IConfigurationElement dec = elements.get(x); RelationshipGroup rGroup = new RelationshipGroup( dec.getDeclaringExtension().getLabel(), dec.getAttribute("label"), dec.createExecutableExtension("class").getClass(), gen, rec, rem, classToClass, classToEnum, classToInterface, enumToClass, enumToEnum, enumToInterface, interfaceToClass, interfaceToEnum, interfaceToInterface); addRelationshipGroup(rGroup); } // load dynamic context actions List<Class> c = PlugIn.getRelationships( ); List<Class> sorted = new ArrayList<Class>( ); for( Class cl : c ) sorted.add( cl ); for( int i = 0; i < sorted.size( ); i++ ) { int smallest = i; RelationshipGroup group = PlugIn.getRelationshipGroup( sorted.get( i ) ); String smallestString = ( group.getSubtype() != null ? group.getSubtype() + " " : "" ) + group.getName(); for( int j = i + 1; j < sorted.size( ); j++ ) { group = PlugIn.getRelationshipGroup( sorted.get( j ) ); String cur = ( group.getSubtype() != null ? group.getSubtype() + " " : "" ) + group.getName(); if( cur.compareTo( smallestString ) < 0 ) { smallest = j; smallestString = cur; } } sorted.add( i, sorted.remove( smallest ) ); } for ( Class partClass : sorted ) { ContextAction action = new AlterRelationshipVisibilityAction(partClass); _actions.add(action); action = new IncrementalExploreSingleAction(partClass); _actions.add(action); } // load plugins: design patterns for (IConfigurationElement element : getConfigElements(DESIGN_PATTERN_ID)) { Object designPattern = element.createExecutableExtension("class"); if (designPattern instanceof DesignPatternGroup) { _designPatterns.add((DesignPatternGroup) designPattern); } } // load plugins: Java types for (IConfigurationElement element : getConfigElements(JAVA_TYPE_ID)) { Object javaType = element.createExecutableExtension("class"); if (javaType instanceof ITypeProperties) { ITypeProperties prop = (ITypeProperties) javaType; _mTypeProperties.put(prop.getLabel(), prop); } else { GreenException.illegalExtensionClass(javaType.getClass(), ITypeProperties.class); } } } catch (Exception e) { e.printStackTrace(); } // add a listener to check for changes to the java model JavaCore.addElementChangedListener(JavaModelListener.getListener()); } /** * @return A list of all the types available in the workspace. This * list can be found by looking in the "Open Type" dialog. */ public static Collection<ITypeProperties> getAvailableTypes() { return _mTypeProperties.values(); } /** * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) */ public void stop(BundleContext context) throws Exception { super.stop(context); } /** * Returns the shared instance. */ public static PlugIn getDefault() { return PLUGIN; } /** * @return the string from the plugin's resource bundle, or 'key' if not * found. */ public static String getResourceString(String key) { ResourceBundle bundle = PlugIn.getDefault().getResourceBundle(); try { return (bundle != null) ? bundle.getString(key) : key; } catch (MissingResourceException e) { return key; } } /** * @return the plugin's resource bundle, */ public ResourceBundle getResourceBundle() { return BUNDLE; } /** * Returns a default progress monitor for methods that require it * * @return a default progress monitor (handlers do nothing). */ public static IProgressMonitor getEmptyProgressMonitor() { return new NullProgressMonitor(); } /** * Returns a relationship's name. * * @param klass - The model's class. * @return The name of the relationship. */ public static String getRelationshipName(Class klass) { return (String) _relationshipMap.get(klass).getName(); } /** * @return A list of all relationships currently loaded. */ public static List<Class> getRelationships() { return new ArrayList<Class>(_relationshipMap.keySet()); } /** * @return A list of all context actions. */ public static List<ContextAction> getActions() { return _actions; } /** * @return The workbench's active window's shell. */ public static Shell getDefaultShell() { return getDefault().getWorkbench().getActiveWorkbenchWindow() .getShell(); } /** * Adds a design pattern. * * @param editor - The editor to use as the selection provider. * @param manager - The menu manager to add the design pattern menu to. */ public static void addDesignPatternMenu( DiagramEditor editor, IMenuManager manager) { MenuManager designPatternMM = new MenuManager(DESIGN_PATTERN_MENU_NAME, null); for (DesignPatternGroup group : _designPatterns) { ContextAction cAction = group.getAction(); cAction.setSelectionProvider(editor); cAction.setContents(); designPatternMM.add(cAction); } manager.add(designPatternMM); } /** * @return True if relationship recognizers are enabled, false otherwise. */ public static boolean isRecognizersEnabled() { return _recognizersEnabled; } /** * Runs code with the relationship recognizers disabled. If any exception * occurs, the relationship recognizers are enabled again and the exception * is rethrown. * * @param runnable - The <code>Runnable</code> to run. * @throws Exception */ public static void runWithoutRecognizers(Runnable runnable) { try { _recognizersEnabled = false; runnable.run(); } finally { _recognizersEnabled = true; } } /** * @param menuGroup - The group to generate the label for * @return a submenu label based on the identifier string */ public static String getSubMenuLabel(String menuGroup) { String menuLabel = menuGroup.substring(menuGroup.lastIndexOf('.') + 1) .toLowerCase(); return menuLabel.substring(0, 1).toUpperCase() + menuLabel.substring(1); } /** * @return The workspace's root. */ public static IWorkspaceRoot getWorkspaceRoot() { return ResourcesPlugin.getWorkspace().getRoot(); } /** * Generates a numerical version number for this plugin. * * @return A mathematical representation of the version number. The digits * are in the form "mmnnss", where m is the major segment, n is the minor * segment, and s is the service segment; the qualifier segment is ignored * for the time being. * @author zgwang */ public static int getVersion() { //TODO Versioning, this should get cleaned up a bit. //Modified to adhere to Eclipse's versioning system as of 10/10/06 //Reminder: in eclipse, version numbers are composed of //four (4) segments: 3 integers and a string respectively //named major.minor.service.qualifier. //the qualifier is usually in the form vYYMMDD String version = (String) getDefault().getBundle().getHeaders().get( Constants.BUNDLE_VERSION); int firstDot = version.indexOf('.'); int secondDot = firstDot + 1 + (version.substring(firstDot + 1)).indexOf('.'); int thirdDot = secondDot + 1 + (version.substring(secondDot + 1)).indexOf('.'); int major = Integer.parseInt(version.substring(0, firstDot)); int minor = Integer.parseInt(version.substring(firstDot + 1, secondDot)); int service; if(thirdDot == secondDot) //no qualifier service = 0; else service = Integer.parseInt(version.substring(secondDot + 1, thirdDot)); return major * 10000 + minor * 100 + service; } /** * @param klass - The <code>AbstractPart</code>'s class * @return The corresponding view part class. */ public static Class getViewPart(Class<?> klass) { return _partToView.get(klass); } /** * @return The plugin's preference information. */ private static IPreferenceStore getPreferences() { return getDefault().getPreferenceStore(); } /** * @param key - The String key * @return The preference from the preference system for the given key. */ public static boolean getBooleanPreference(String key) { return getPreferences().getBoolean(key); } /** * @param key The String key * @param value The value to assign */ public static void setBooleanPreference(String key, boolean value) { getPreferences().setValue( key, value ); for( DiagramEditor e : DiagramEditor.getEditors( ) ) e.refresh( ); } /** * @param key - The String key * @return The preference from the preference system for the given key. */ public static String getPreference(String key) { return getPreferences().getString(key); } /** * Gets the Font object based on the preferences settings * @param key - The String key * @return The preference from the preference system for the given key. * * @author zgwang */ public static Font getFontPreference(String key, boolean forceItalics) { int fontSize = 10; int style = 0; int italics = forceItalics ? 2 : 0; String fontData = getPreference(key); String fontName = ""; StringTokenizer tokens = new StringTokenizer("0" + fontData, "|"); tokens.nextToken(); fontName = tokens.nextToken(); //font = <unknown>|<name>|<size>|<style>|<OS>|<don't cares> fontSize = (int)(Double.parseDouble(tokens.nextToken())); //Get italics setting from parameter, retain bold setting style = italics; if(tokens.hasMoreTokens()) { style += Integer.parseInt(tokens.nextToken()) % 2; } return new Font(null, fontName, fontSize, style); } /** * @param key - The String key * @return The preference from the preference system for the given key. */ public static Color getColorPreference(String key) { String col = getPreference(key); int comma1 = col.indexOf(','); int comma2 = col.lastIndexOf(','); int r = Integer.parseInt(col.substring(0, comma1)); int g = Integer.parseInt(col.substring(comma1 + 1, comma2)); int b = Integer.parseInt(col.substring(comma2 + 1)); return new Color(null, r, g, b); } /** * @param key - The String key * @return The preference from the preference system for the given key. */ public static int getIntegerPreference(String key) { return getPreferences().getInt(key); } /** * @param extension - The file extension. * @return The save format for the given extension. */ public static ISaveFormat getSaveFormat(String extension) { return SAVE_FORMAT_MAP.get(extension); } /** * @return A list of all available save formats. */ public static List<String> getSaveFormats() { Set<String> formats = new HashSet<String>(SAVE_FORMAT_MAP.keySet()); List<String> orderedFormats = new ArrayList<String>(); formats.remove(PluginConstants.GREEN_EXTENSION); orderedFormats.add(PluginConstants.GREEN_EXTENSION); while (formats.size() > 0) { String last = "{"; for (String string : formats) { if (string.compareTo(last) < 0) { last = string; } } formats.remove(last); orderedFormats.add(last); } return orderedFormats; } /** * @return A list of all the plugins that extend the extension point * edu.buffalo.cse.green.relationships. */ public static List<RelationshipGroup> getRelationshipGroups() { List<RelationshipGroup> groups = new ArrayList<RelationshipGroup>(); groups.addAll(_relationshipMap.values()); return groups; } /** * Determines whether or not a given model is filtered out from the diagram. * * @param model - The given model. * @return true if the model is filtered out, false otherwise. */ public static boolean filterMember(MemberModel model) { if (model == null) return false; for (MemberFilter filter : getMemberFilters()) { if (filter.isFiltered(model.getMember())) return true; } return false; } // public static Set<MemberModel> filterMembers( // Collection<MemberModel> models) { // Set<MemberModel> filtered = new HashSet<MemberModel>(); // // for (MemberFilter filter : getMemberFilters()) { // for (MemberModel model : models) { // if (filter.isFiltered(model.getMember())) { // filtered.add(model); // } // } // } // // return filtered; // } public static List<MemberFilter> getMemberFilters() { List<MemberFilter> filters = new ArrayList<MemberFilter>(); try { StringTokenizer tokens = new StringTokenizer( PlugIn.getPreference(P_FILTERS_MEMBER), "|"); while (tokens.hasMoreTokens()) { String token = tokens.nextToken(); filters.add(new MemberFilter(token)); } } catch (Exception e) { GreenException.warn("problem loading filters"); } return filters; } /** * @return A list of all the relationship plugins. */ public static List<RelationshipGroup> getRelationshipList() { return _relationshipGroups; } /** * @return The mapping from <code>String</code> representation of kinds of * types to their respective plugin instances. */ public static Map<String, ITypeProperties> getTypeProperties() { return _mTypeProperties; } }