From 71f0195f3973281ce85e4838ccf04ac6c58f05eb Mon Sep 17 00:00:00 2001 From: Nils Christian Ehmke <nie@informatik.uni-kiel.de> Date: Tue, 21 May 2013 16:44:42 +0200 Subject: [PATCH] Refactoring, modifications for the quality tools, and minor bug fixing --- .../.settings/org.eclipse.jdt.ui.prefs | 2 +- .../webgui/domain/ComponentListContainer.java | 5 + .../persistence/impl/FSProjectDAOImpl.java | 15 + .../util/Class2ModelInstanceConverter.java | 18 +- .../webgui/service/IProjectService.java | 3 + .../webgui/service/impl/util/ACManager.java | 12 +- .../webgui/service/impl/util/Analysis.java | 10 +- .../application/GlobalPropertiesBean.java | 27 + .../beans/view/CurrentAnalysisEditorBean.java | 515 +++++++++--------- .../view/CurrentAnalysisEditorGraphBean.java | 488 +++++++++-------- .../dialogs/AnalysisEditorPageDialogs.xhtml | 2 +- .../webapp/pages/AnalysisEditorPage.xhtml | 6 +- 12 files changed, 587 insertions(+), 516 deletions(-) diff --git a/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs b/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs index e8289876..87c76d67 100644 --- a/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs +++ b/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs @@ -89,7 +89,7 @@ sp_cleanup.never_use_blocks=false sp_cleanup.never_use_parentheses_in_expressions=false sp_cleanup.on_save_use_additional_actions=true sp_cleanup.organize_imports=true -sp_cleanup.qualify_static_field_accesses_with_declaring_class=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true sp_cleanup.qualify_static_member_accesses_with_declaring_class=true diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/domain/ComponentListContainer.java b/Kieker.WebGUI/src/main/java/kieker/webgui/domain/ComponentListContainer.java index 79f4ba60..2b0aaa86 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/domain/ComponentListContainer.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/domain/ComponentListContainer.java @@ -15,6 +15,7 @@ ***************************************************************************/ package kieker.webgui.domain; +import java.util.Collections; import java.util.List; import kieker.webgui.domain.pluginDecorators.FilterDecorator; @@ -30,6 +31,10 @@ import kieker.webgui.domain.pluginDecorators.RepositoryDecorator; */ public class ComponentListContainer { + /** This constant represents an immutable and empty container. */ + public static final ComponentListContainer EMPTY_CONTAINER = new ComponentListContainer(Collections.<ReaderDecorator>emptyList(), + Collections.<FilterDecorator>emptyList(), Collections.<RepositoryDecorator>emptyList()); + private final List<ReaderDecorator> readers; private final List<FilterDecorator> filters; private final List<RepositoryDecorator> repositories; diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java index 4e448541..13c7f150 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java @@ -887,6 +887,11 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { } + /** + * A simple function to convert repository classes to model instances. + * + * @author Nils Christian Ehmke + */ private class ConvertRepositoryClass2ModelInstanceFunction implements Function<Class<AbstractRepository>, RepositoryDecorator> { private final ClassAndMethodContainer classAndMethodContainer; @@ -906,6 +911,11 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { } + /** + * A simple function to convert reader classes to model instances. + * + * @author Nils Christian Ehmke + */ private class ConvertReaderClass2ModelInstanceFunction implements Function<Class<AbstractReaderPlugin>, ReaderDecorator> { private final ClassAndMethodContainer classAndMethodContainer; @@ -925,6 +935,11 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener { } + /** + * A simple function to convert filter classes to model instances. + * + * @author Nils Christian Ehmke + */ private class ConvertFilterClass2ModelInstanceFunction implements Function<Class<AbstractFilterPlugin>, FilterDecorator> { private final ClassAndMethodContainer classAndMethodContainer; diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/Class2ModelInstanceConverter.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/Class2ModelInstanceConverter.java index 9cd4a18f..c2753c05 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/Class2ModelInstanceConverter.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/Class2ModelInstanceConverter.java @@ -34,6 +34,7 @@ import kieker.analysis.model.analysisMetaModel.MIPlugin; import kieker.analysis.model.analysisMetaModel.MIProperty; import kieker.analysis.model.analysisMetaModel.MIReader; import kieker.analysis.model.analysisMetaModel.MIRepository; +import kieker.analysis.model.analysisMetaModel.MIRepositoryConnector; import kieker.analysis.plugin.AbstractPlugin; import kieker.analysis.plugin.filter.AbstractFilterPlugin; import kieker.analysis.plugin.reader.AbstractReaderPlugin; @@ -122,6 +123,7 @@ public class Class2ModelInstanceConverter { final Collection<MIInputPort> inputPorts = new ArrayList<MIInputPort>(); final Collection<MIOutputPort> outputPorts = new ArrayList<MIOutputPort>(); final Collection<MIDisplay> displays = new ArrayList<MIDisplay>(); + final Collection<MIRepositoryConnector> repositories = new ArrayList<MIRepositoryConnector>(); final Map<String, String> propertyDescriptions = new HashMap<String, String>(); final Map<String, String> displayDescriptions = new HashMap<String, String>(); String description = ""; @@ -131,14 +133,15 @@ public class Class2ModelInstanceConverter { try { description = this.fillDescription(clazz, classAndMethodContainer); dependency = this.fillDependency(clazz, classAndMethodContainer); - this.fillProperties(clazz, classAndMethodContainer, properties, propertyDescriptions); plugin.getProperties().addAll(properties); if ((type == Type.Filter) || (type == Type.Reader)) { this.fillOutputPorts((Class<AbstractPlugin>) clazz, classAndMethodContainer, outputPorts, (MIPlugin) plugin); this.fillDisplays((Class<AbstractPlugin>) clazz, classAndMethodContainer, displays, displayDescriptions); + this.fillRepositories((Class<AbstractPlugin>) clazz, classAndMethodContainer, repositories); ((MIPlugin) plugin).getOutputPorts().addAll(outputPorts); ((MIPlugin) plugin).getDisplays().addAll(displays); + ((MIPlugin) plugin).getRepositories().addAll(repositories); if (type == Type.Filter) { this.fillInputPorts((Class<AbstractFilterPlugin>) clazz, classAndMethodContainer, inputPorts, (MIFilter) plugin); ((MIFilter) plugin).getInputPorts().addAll(inputPorts); @@ -255,6 +258,19 @@ public class Class2ModelInstanceConverter { } } + private void fillRepositories(final Class<? extends AbstractPlugin> clazz, final ClassAndMethodContainer classAndMethodContainer, + final Collection<MIRepositoryConnector> repositories) { + final Annotation annotation = this.getSuitableAnnotation(clazz, classAndMethodContainer); + final Annotation[] repositoryPortAnnotations = (Annotation[]) new Mirror().on(annotation).invoke().method("repositoryPorts").withoutArgs(); + + for (final Annotation repositoryPortAnnotation : repositoryPortAnnotations) { + final MIRepositoryConnector newConnector = Class2ModelInstanceConverter.FACTORY.createRepositoryConnector(); + newConnector.setName((String) new Mirror().on(repositoryPortAnnotation).invoke().method("name").withoutArgs()); + + repositories.add(newConnector); + } + } + private String fillDependency(final Class<? extends AbstractAnalysisComponent> clazz, final ClassAndMethodContainer classAndMethodContainer) { final Annotation annotation = this.getSuitableAnnotation(clazz, classAndMethodContainer); return (String) new Mirror().on(annotation).invoke().method("dependencies").withoutArgs(); diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java index ec59a585..638ee087 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java @@ -61,6 +61,9 @@ public interface IProjectService { @PreAuthorize("hasAnyRole('User', 'Administrator')") public void addProject(final String projectName, final String username) throws ProjectAlreadyExistingException, IOException; + /** + * Updates the displays of all analyses. + */ public void updateAllAnalyses(); /** diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/ACManager.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/ACManager.java index afc37675..412b0062 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/ACManager.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/ACManager.java @@ -223,10 +223,16 @@ public class ACManager { return retState; } + /** + * Updates the displays from the given project. + * + * @param projectName + * The project to update. + */ public void updateDisplays(final String projectName) { - if (this.analyses.containsKey(projectName)) { - this.analyses.get(projectName).updateDisplays(); - } + // if (this.analyses.containsKey(projectName)) { + // this.analyses.get(projectName).updateDisplays(); + // } } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/Analysis.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/Analysis.java index b8d71dcc..a5c9778b 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/Analysis.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/util/Analysis.java @@ -18,8 +18,6 @@ package kieker.webgui.service.impl.util; import java.io.File; import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; import kieker.analysis.AnalysisController; import kieker.common.logging.Log; @@ -47,8 +45,8 @@ public class Analysis { private final Object analysisController; private final Object analysisControllerThread; - private final Map<String, Map<String, Object>> displayObjects = new HashMap<String, Map<String, Object>>(); - private final Map<String, Map<String, Method>> displayMethods = new HashMap<String, Map<String, Method>>(); + // private final Map<String, Map<String, Object>> displayObjects = new HashMap<String, Map<String, Object>>(); + // private final Map<String, Map<String, Method>> displayMethods = new HashMap<String, Map<String, Method>>(); /** * Creates a new instance of this class using the given parameters. @@ -152,7 +150,7 @@ public class Analysis { AnalysisController.class.getName()); } - public void updateDisplays() { + // public void updateDisplays() { - } + // } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/GlobalPropertiesBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/GlobalPropertiesBean.java index 99af78d6..ae797573 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/GlobalPropertiesBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/GlobalPropertiesBean.java @@ -18,12 +18,15 @@ package kieker.webgui.web.beans.application; import java.io.Serializable; import java.util.Locale; +import java.util.Map; import java.util.ResourceBundle; import javax.faces.application.FacesMessage; import javax.faces.application.FacesMessage.Severity; import javax.faces.context.FacesContext; +import kieker.common.record.AbstractMonitoringRecord; + import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -217,4 +220,28 @@ public class GlobalPropertiesBean implements Serializable { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(severity, "", msg)); } + /** + * This method reads parameters from the request parameter map of the current context. The parameters are converted to actual objects, based on the given types. + * It is assumed (but not checked) that both arrays have the same size. + * + * @param parameterNames + * The name of the parameters within the request parameter map. + * @param types + * The actual types of the parameters. The method will try to convert the strings to those types. + * + * @return An array containing the actual parameter objects. + */ + public static Object[] convertObjectsFromParameterMap(final String[] parameterNames, final Class<?>[] types) { + final int numberOfObjects = parameterNames.length; + final Map<String, String> parameterMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); + final String[] parameters = new String[numberOfObjects]; + + for (int i = 0; i < numberOfObjects; i++) { + parameters[i] = parameterMap.get(parameterNames[i]); + + } + + // We can borrow a method from the Kieker framework to convert the strings + return AbstractMonitoringRecord.fromStringArrayToTypedArray(parameters, types); + } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java index 9661a3e7..f6ee5a48 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorBean.java @@ -18,7 +18,6 @@ package kieker.webgui.web.beans.view; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,9 +66,10 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; /** - * The {@link CurrentAnalysisEditorBean} contains the necessary data behind an instance of the analysis editor. It provides various methods to manipulate the current - * project, as the analysis editor is the most important part of the whole application. The connection to the graph within the editor is done via another bean (the - * {@link CurrentAnalysisEditorGraphBean}).<br> + * This bean contains the necessary data behind an instance of the analysis editor. It provides various methods to manipulate the current project with respect to the + * analysis components. The connection to the graph within the editor is done with {@link CurrentAnalysisEditorGraphBean}. <br> + * </br> + * * The class is a Spring managed bean with view scope to make sure that one user (even in one session) can open multiple projects at a time without causing any * problems. * @@ -79,16 +79,23 @@ import org.springframework.stereotype.Component; @Scope("view") public class CurrentAnalysisEditorBean { + private static final MIAnalysisMetaModelFactory MODEL_FACTORY = MAnalysisMetaModelFactory.eINSTANCE; private static final Log LOG = LogFactory.getLog(CurrentAnalysisEditorBean.class); - private static final MIAnalysisMetaModelFactory FACTORY = MAnalysisMetaModelFactory.eINSTANCE; + + private static final String[] PARAMETER_NAMES_SAVE_PROJECT = { "overwriteNewerProject", "layoutString" }; + private static final Class<?>[] PARAMETER_TYPES_SAVE_PROJECT = { Boolean.class, String.class }; + + private static final String JS_CMD_SHOW_FORCE_SAVE_DIALOG = "forceSaveDlg.show()"; private MIAnalysisComponent globalConfigurationInstance; - private ComponentListContainer availableComponents; private MIAnalysisComponent selectedComponent; - private boolean unsavedModifications; private MIProject project; + + private ComponentListContainer availableComponents; + + private boolean unsavedModifications; private String projectName; - private long timeStamp; + private long timeStampFromLastSaving; @Autowired private CurrentAnalysisEditorGraphBean currentAnalysisEditorGraphBean; @@ -100,14 +107,12 @@ public class CurrentAnalysisEditorBean { private ProjectsBean projectsBean; @Autowired private UserBean userBean; - private String currentLayout; /** * Creates a new instance of this class. <b>Do not call this constructor manually. It will only be accessed by Spring.</b> */ public CurrentAnalysisEditorBean() { - this.availableComponents = new ComponentListContainer(Collections.<ReaderDecorator>emptyList(), Collections.<FilterDecorator>emptyList(), - Collections.<RepositoryDecorator>emptyList()); + this.availableComponents = ComponentListContainer.EMPTY_CONTAINER; } /** @@ -116,77 +121,111 @@ public class CurrentAnalysisEditorBean { */ public void initialize() { try { - // Make sure that the initialization will only be done for the init request. During all other requests, the method call have to be ignored. + // Make sure that the initialization will only be done for the initial request. During all other requests, the method call has to be ignored. if (!FacesContext.getCurrentInstance().isPostback()) { - // Load the project itself - this.project = this.projectsBean.openProject(this.projectName); - // Remember the current time! This is important for the later comparison of the time stamps. - this.resetTimeStamp(); - // Add the libraries within the lib-folder to the current model - this.initializeModelLibraries(); - // Load the available readers, filters and repositories + this.loadProject(); + this.enrichProjectWithLibraries(); this.reloadAvailableComponents(); - this.unsavedModifications = false; + this.clearModificationsFlag(); + this.resetTimeStampFromLastSaving(); } } catch (final ProjectLoadException ex) { CurrentAnalysisEditorBean.LOG.error("An error occured while loading the project.", ex); - // FacesContext.getCurrentInstance().getApplication().getNavigationHandler().handleNavigation(FacesContext.getCurrentInstance(), null, - // "projectOverview"); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgProjectLoadingException()); } catch (final NullPointerException ex) { - // This exception can occur, when a property has not been initialized + // This exception can occur when a property has not been initialized CurrentAnalysisEditorBean.LOG.error("An error occured while loading the project.", ex); - // FacesContext.getCurrentInstance().getApplication().getNavigationHandler().handleNavigation(FacesContext.getCurrentInstance(), null, - // "projectOverview"); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgProjectLoadingException()); } } /** - * This method takes all libraries from the lib-folder and adds them to the in-memory-model. - * - * @throws ProjectLoadException - * If something went wrong during the loading of the libraries. + * This method initializes the graph by delivering the necessary JavaScript commands to the client. It prints all current existing plugins, repositories and + * their connections. */ - private void initializeModelLibraries() throws ProjectLoadException { - try { - final List<String> libs = this.projectService.listAllLibraries(this.projectName); - // Add them, but remove all existing dependencies so far to avoid double entries. This also makes sure that the model - after it has been opened - - // points just to valid dependencies (and to all of them). - this.project.getDependencies().clear(); - for (final String lib : libs) { - final MIDependency dep = CurrentAnalysisEditorBean.FACTORY.createDependency(); - dep.setFilePath(lib); - this.project.getDependencies().add(dep); + public void initializeGraph() { + // Make sure that the currentAnalysisEditorGraphBean knows "this" as well. + this.currentAnalysisEditorGraphBean.setCurrentAnalysisEditorBean(this); + + // Initialize the graph + this.currentAnalysisEditorGraphBean.declareGraph(); + + // Initialize the component for the project configuration + this.initializeGlobalConfigurationInstance(); + this.currentAnalysisEditorGraphBean.addGlobalConfigurationInstance(this.globalConfigurationInstance); + + // Initialize the reader, filter and repositories + for (final MIPlugin plugin : this.project.getPlugins()) { + if (plugin instanceof MIReader) { + this.currentAnalysisEditorGraphBean.addReader((MIReader) plugin); + } else { + this.currentAnalysisEditorGraphBean.addFilter((MIFilter) plugin); } - } catch (final ProjectNotExistingException ex) { - throw new ProjectLoadException("The project does not exist.", ex); } + + for (final MIRepository repository : this.project.getRepositories()) { + this.currentAnalysisEditorGraphBean.addRepository(repository); + } + + // Now initialize the connections between filters... + for (final MIPlugin plugin : this.project.getPlugins()) { + for (final MIOutputPort oPort : plugin.getOutputPorts()) { + for (final MIInputPort iPort : oPort.getSubscribers()) { + this.currentAnalysisEditorGraphBean.addConnection(plugin, iPort.getParent(), oPort, iPort); + } + } + } + + // ...and between filters and repositories + for (final MIPlugin plugin : this.project.getPlugins()) { + for (final MIRepositoryConnector rPort : plugin.getRepositories()) { + // It is possible that the connected repository is null, if it hasn't been set yet. Check this. + if (rPort.getRepository() != null) { + this.currentAnalysisEditorGraphBean.addConnection(plugin, rPort.getRepository(), rPort); + } + } + } + + // Initialize the mouse click and the edge constraint listeners + this.currentAnalysisEditorGraphBean.initListeners(); + this.currentAnalysisEditorGraphBean.initEdgeConstraints(); + + // Now we have to set the default grid size and color of the user + this.currentAnalysisEditorGraphBean.setGridColor(this.userBean.getGridColor()); + this.currentAnalysisEditorGraphBean.setGridSize(this.userBean.getGridSize()); + + // Perform either an initial auto layout or use the saved layout - if it exists + final String layout = this.projectService.getAnalysisLayout(this.projectName); + + if (layout != null) { + this.currentAnalysisEditorGraphBean.loadLayout(layout); + } else { + this.currentAnalysisEditorGraphBean.startAutoLayout(); + } + + // Make sure that guests cannot modify the graph + this.currentAnalysisEditorGraphBean.checkReadOnlyForGuests(); } /** - * This method is the handler for the file upload. It tries to upload the given file and informs the user via the growl-component. + * This method is the handler for the file upload. It tries to upload the given file as a library and informs the user via the growl-component. * * @param event * The upload event. */ - public void handleFileUpload(final FileUploadEvent event) { - // Get the file from the event - final UploadedFile file = event.getFile(); - + public void handleLibraryFileUpload(final FileUploadEvent event) { try { - // Use the file system manager to upload the new file - final MIDependency lib; - this.projectService.uploadLibrary(file, this.projectName); + final UploadedFile uploadedFile = event.getFile(); + + // Delegate the task to upload the library + this.projectService.uploadLibrary(uploadedFile, this.projectName); + + // Seems like it worked. We can add the library to our model and inform the user. GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgLibraryUploaded()); - // As it seem to have worked, we can add the library to our model. - lib = CurrentAnalysisEditorBean.FACTORY.createDependency(); - lib.setFilePath(file.getFileName()); - this.project.getDependencies().add(lib); - // Update our class loader and the available plugins & repositories + this.enrichProjectWithLibrary(uploadedFile.getFileName()); - // We have to reinitialize the tool palette completely! This is necessary as some of the already existing classes could need the newly loaded classes. + // We have to reinitialize the available components! This is necessary as some of the already existing classes could need the newly loaded classes. this.reloadAvailableComponents(); this.setModificationsFlag(); @@ -206,18 +245,16 @@ public class CurrentAnalysisEditorBean { } /** - * Removes the library with the given name and reloads the classes and the classloader. Furthermore the tool palette will be reinitalized. + * Removes the library with the given name and reloads the available components. * * @param name * The name of the library to be removed. */ public void deleteLibrary(final String name) { try { + // Delegate the task if (this.projectService.deleteLibrary(this.projectName, name)) { - // We have to reinitialize the tool palette completely! This is necessary as some of the already existing classes could need the newly loaded - // classes. this.reloadAvailableComponents(); - this.setModificationsFlag(); } } catch (final IOException ex) { @@ -227,46 +264,7 @@ public class CurrentAnalysisEditorBean { } /** - * This method loads the list of available readers, filters and repositories, using the current libraries within the model. - */ - private void reloadAvailableComponents() { - this.availableComponents = this.projectService.getAvailableComponents(this.projectName); - } - - public MIProject getProject() { - return this.project; - } - - public void setProjectName(final String newName) { - // Remember the given parameters - this.projectName = newName; - } - - public String getProjectName() { - return this.projectName; - } - - public ComponentListContainer getAvailableComponents() { - return this.availableComponents; - } - - /** - * This method sets the time stamp to the current system time. - */ - public void resetTimeStamp() { - this.timeStamp = System.currentTimeMillis(); - } - - public long getTimeStamp() { - return this.timeStamp; - } - - public MIAnalysisComponent getSelectedPlugin() { - return this.selectedComponent; - } - - /** - * This method delivers the available libraries of this project as a pair of strings. The first element is always the kieker-library. + * This method delivers the available libraries of this project. The first element is always the kieker-library. * * @return The available libraries. */ @@ -287,30 +285,30 @@ public class CurrentAnalysisEditorBean { /** * This method tries to save the current project and informs the user about success or fail. There should be two parameters within the request parameter map - * (layoutString and overwriteNewerProject) as this method is called via javascript. + * (layoutString and overwriteNewerProject) as this method is called technically from JavaScript. */ public void saveProject() { // Get the parameters - final Map<String, String> paramMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); - this.currentLayout = paramMap.get("layoutString"); - final boolean overwriteNewerProject = Boolean.parseBoolean(paramMap.get("overwriteNewerProject")); + final Object[] parameters = GlobalPropertiesBean.convertObjectsFromParameterMap(PARAMETER_NAMES_SAVE_PROJECT, PARAMETER_TYPES_SAVE_PROJECT); + final boolean overwriteNewerProject = (Boolean) parameters[0]; + final String currentLayout = (String) parameters[1]; - // Convert the global component property to project properties + // Add the project configuration to the project, as those are stored within the global configuration component. this.project.getProperties().clear(); for (final MIProperty mProperty : this.globalConfigurationInstance.getProperties()) { - final MIProperty mCopy = CurrentAnalysisEditorBean.FACTORY.createProperty(); + final MIProperty mCopy = MODEL_FACTORY.createProperty(); mCopy.setName(mProperty.getName()); mCopy.setValue(mProperty.getValue()); + this.project.getProperties().add(mCopy); } try { - this.projectService.saveProject(this.projectName, this.project, this.timeStamp, overwriteNewerProject, this.userBean.getUsername(), this.currentLayout, - null); + this.projectService.saveProject(this.projectName, this.project, this.timeStampFromLastSaving, overwriteNewerProject, this.userBean.getUsername(), + currentLayout, null); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgProjectSaved()); - // Update the time stamp! - this.resetTimeStamp(); + this.resetTimeStampFromLastSaving(); this.clearModificationsFlag(); } catch (final IOException ex) { CurrentAnalysisEditorBean.LOG.error("An error occured while saving the project.", ex); @@ -319,7 +317,7 @@ public class CurrentAnalysisEditorBean { CurrentAnalysisEditorBean.LOG.info("The project has been modified externally in the meanwhile.", ex); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_WARN, this.globalPropertiesBean.getMsgProjectModified()); // Give the user the possibility to force-save the project - RequestContext.getCurrentInstance().execute("forceSaveDlg.show()"); + RequestContext.getCurrentInstance().execute(JS_CMD_SHOW_FORCE_SAVE_DIALOG); } catch (final ProjectNotExistingException ex) { CurrentAnalysisEditorBean.LOG.error("The project does not exist.", ex); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_ERROR, this.globalPropertiesBean.getMsgProjectNotExistingException()); @@ -327,14 +325,14 @@ public class CurrentAnalysisEditorBean { } /** - * This method adds a new repository to the current model, using the given instance of {@code RepositoryContainer} for it. + * This method adds a new repository to the current model, using the given container to create it. * * @param container * The container which delivers the copy of the repository. */ public void addRepository(final RepositoryDecorator container) { // Create a new instance for the model - final MIRepository repository = container.newCopy(CurrentAnalysisEditorBean.FACTORY); + final MIRepository repository = container.newCopy(CurrentAnalysisEditorBean.MODEL_FACTORY); // Add it to the project - and to the graph this.project.getRepositories().add(repository); @@ -343,9 +341,15 @@ public class CurrentAnalysisEditorBean { this.setModificationsFlag(); } + /** + * This method adds a new reader to the current model, using the given container to create it. + * + * @param container + * The container which delivers the copy of the reader. + */ public void addReader(final ReaderDecorator container) { // Create a new instance for the model - final MIPlugin plugin = container.newCopy(CurrentAnalysisEditorBean.FACTORY); + final MIPlugin plugin = container.newCopy(CurrentAnalysisEditorBean.MODEL_FACTORY); // Add it to the project - and to the graph this.project.getPlugins().add(plugin); @@ -355,14 +359,14 @@ public class CurrentAnalysisEditorBean { } /** - * This method adds a new plugin to the current model, using the given instance of {@code PluginContainer} for it. + * This method adds a new filter to the current model, using the given container to create it. * * @param container - * The container which delivers the copy of the plugin. + * The container which delivers the copy of the filter. */ public void addFilter(final FilterDecorator container) { // Create a new instance for the model - final MIPlugin plugin = container.newCopy(CurrentAnalysisEditorBean.FACTORY); + final MIPlugin plugin = container.newCopy(CurrentAnalysisEditorBean.MODEL_FACTORY); // Add it to the project - and to the graph this.project.getPlugins().add(plugin); @@ -371,39 +375,12 @@ public class CurrentAnalysisEditorBean { this.setModificationsFlag(); } - /** - * This method sets the property {@link CurrentAnalysisEditorBean#unsavedModifications} to false and refreshes the necessary components within the analysis - * editor to make this visible. - */ - private void clearModificationsFlag() { - this.unsavedModifications = false; - RequestContext.getCurrentInstance().update("menuForm"); - } - - /** - * This method sets the property {@link CurrentAnalysisEditorBean#unsavedModifications} to true and refreshes the necessary components within the analysis editor - * to make this visible. - */ - private void setModificationsFlag() { - this.unsavedModifications = true; - RequestContext.getCurrentInstance().update("menuForm"); - } - - /** - * Getter for the property {@link CurrentAnalysisEditorBean#unsavedModifications}. - * - * @return The current value of the property. - */ - public boolean isUnsavedModification() { - return this.unsavedModifications; - } - /** * This method delivers the properties of the currently selected plugin, but it adds also the name- and the class-properties as a string to the list. The first - * element is always the classname, the second one is always the pluginname. + * element is always the class name, the second one is always the plugin name. If the currently selected component is the global configuration instance though, + * those properties will not be added. * - * @return A list with all properties of the plugin plus the name- and class-properties. If no plugin is selected, the list of the selected repository is - * delivered. + * @return A list with all properties of the plugin plus the name- and class-properties. */ public List<Object> getAdvancedPluginProperties() { final List<Object> result = new ArrayList<Object>(); @@ -420,25 +397,25 @@ public class CurrentAnalysisEditorBean { return result; } - public boolean isGlobalConfigComponentSelected() { - return this.selectedComponent == this.globalConfigurationInstance; - } - /** - * Delivers a human readable description of the given property. + * Delivers a human readable description of the given property.<br> + * + * </br> The current implementation is not very fast, as it searches through all available components. If + * necessary this method can be modified in order to run faster (for example by using hash maps). * * @param component * The parent of the property. * @param property * The property name. + * * @return A human readable description and a substitution if there is no description. */ public String getDescription(final MIAnalysisComponent component, final String property) { + final String className = component.getClassname(); AbstractAnalysisComponentDecorator<? extends MIAnalysisComponent> container = null; // Find the container which contains the component if (component instanceof MIReader) { - final String className = ((MIReader) component).getClassname(); for (final ReaderDecorator reader : this.availableComponents.getReaders()) { if (reader.getClassname().equals(className)) { container = reader; @@ -446,7 +423,6 @@ public class CurrentAnalysisEditorBean { } } } else if (component instanceof MIFilter) { - final String className = ((MIFilter) component).getClassname(); for (final FilterDecorator filter : this.availableComponents.getFilters()) { if (filter.getClassname().equals(className)) { container = filter; @@ -454,7 +430,6 @@ public class CurrentAnalysisEditorBean { } } } else { - final String className = ((MIRepository) component).getClassname(); for (final RepositoryDecorator repository : this.availableComponents.getRepositories()) { if (repository.getClassname().equals(className)) { container = repository; @@ -472,113 +447,6 @@ public class CurrentAnalysisEditorBean { return "No description available."; } - /** - * This method should be called if the grid color has been modified. - * - * @param event - * The change event. - */ - public void gridColorListener(final ValueChangeEvent event) { - this.currentAnalysisEditorGraphBean.setGridColor((String) event.getNewValue()); - } - - /** - * This method should be called if the grid size has been modified. - * - * @param event - * The change event. - */ - public void gridSizeListener(final ValueChangeEvent event) { - this.currentAnalysisEditorGraphBean.setGridSize((Integer) event.getNewValue()); - } - - /** - * This method initializes the modified jit-graph by delivering the necessary javascript commands to the client. It prints all current existing plugins, - * repositories and their connections. All further components will be added mostly by other methods. - */ - public void initializeGraph() { - this.initializeGlobalConfigurationInstance(); - - // Make sure that the currentAnalysisEditorGraphBean knows "this" as well. - this.currentAnalysisEditorGraphBean.setCurrentAnalysisEditorBean(this); - - // Initialize the graph - this.currentAnalysisEditorGraphBean.declareGraph(); - - // Initialize the reader, filter and repositories - for (final MIPlugin plugin : this.project.getPlugins()) { - if (plugin instanceof MIReader) { - this.currentAnalysisEditorGraphBean.addReader((MIReader) plugin); - } else { - this.currentAnalysisEditorGraphBean.addFilter((MIFilter) plugin); - } - } - - for (final MIRepository repository : this.project.getRepositories()) { - this.currentAnalysisEditorGraphBean.addRepository(repository); - } - - // Now initialize the connections between filters... - for (final MIPlugin plugin : this.project.getPlugins()) { - for (final MIOutputPort oPort : plugin.getOutputPorts()) { - for (final MIInputPort iPort : oPort.getSubscribers()) { - this.currentAnalysisEditorGraphBean.addConnection(plugin, iPort.getParent(), oPort, iPort); - } - } - } - - // ...and between filters and repositories - for (final MIPlugin plugin : this.project.getPlugins()) { - for (final MIRepositoryConnector rPort : plugin.getRepositories()) { - // It is possible that the connected repository is null, if it hasn't been set yet. Check this. - if (rPort.getRepository() != null) { - this.currentAnalysisEditorGraphBean.addConnection(plugin, rPort.getRepository(), rPort); - } - } - } - - this.currentAnalysisEditorGraphBean.initListeners(); - this.currentAnalysisEditorGraphBean.addEdgeConstraints(); - - // Now we have to set the default grid size and color of the user - this.currentAnalysisEditorGraphBean.setGridColor(this.userBean.getGridColor()); - this.currentAnalysisEditorGraphBean.setGridSize(this.userBean.getGridSize()); - - // Perform either an initial auto layout or use the saved layout - if it exists - final String layout = this.projectService.getAnalysisLayout(this.projectName); - - if (layout != null) { - this.currentAnalysisEditorGraphBean.loadLayout(layout); - } else { - this.currentAnalysisEditorGraphBean.startAutoLayout(); - } - - this.currentAnalysisEditorGraphBean.checkReadOnlyForGuests(); - } - - private void initializeGlobalConfigurationInstance() { - this.globalConfigurationInstance = CurrentAnalysisEditorBean.FACTORY.createFilter(); - - final kieker.analysis.annotation.AnalysisController annotation = AnalysisController.class.getAnnotation(kieker.analysis.annotation.AnalysisController.class); - final Property[] properties = annotation.configuration(); - final Map<String, String> propertyMap = new HashMap<String, String>(); - - for (final Property property : properties) { - propertyMap.put(property.name(), property.defaultValue()); - } - - for (final MIProperty mProperty : this.project.getProperties()) { - propertyMap.put(mProperty.getName(), mProperty.getValue()); - } - - for (final Map.Entry<String, String> property : propertyMap.entrySet()) { - final MIProperty mProperty = CurrentAnalysisEditorBean.FACTORY.createProperty(); - mProperty.setName(property.getKey()); - mProperty.setValue(property.getValue()); - this.globalConfigurationInstance.getProperties().add(mProperty); - } - } - /** * This method should be called if a node (plugin, repository) has been selected. * @@ -690,8 +558,129 @@ public class CurrentAnalysisEditorBean { this.setModificationsFlag(); } - public MIAnalysisComponent getGlobalConfigurationComponent() { - return this.globalConfigurationInstance; + /** + * This method should be called if the grid color has been modified. + * + * @param event + * The change event. + */ + public void gridColorListener(final ValueChangeEvent event) { + this.currentAnalysisEditorGraphBean.setGridColor((String) event.getNewValue()); + } + + /** + * This method should be called if the grid size has been modified. + * + * @param event + * The change event. + */ + public void gridSizeListener(final ValueChangeEvent event) { + this.currentAnalysisEditorGraphBean.setGridSize((Integer) event.getNewValue()); + } + + public boolean isUnsavedModification() { + return this.unsavedModifications; + } + + public boolean isGlobalConfigComponentSelected() { + return this.selectedComponent == this.globalConfigurationInstance; + } + + public MIAnalysisComponent getSelectedPlugin() { + return this.selectedComponent; + } + + public MIProject getProject() { + return this.project; + } + + public void setProjectName(final String projectName) { + this.projectName = projectName; + } + + public String getProjectName() { + return this.projectName; + } + + public ComponentListContainer getAvailableComponents() { + return this.availableComponents; + } + + private void reloadAvailableComponents() { + this.availableComponents = this.projectService.getAvailableComponents(this.projectName); + } + + private void loadProject() { + this.project = this.projectsBean.openProject(this.projectName); + } + + private void enrichProjectWithLibraries() throws ProjectLoadException { + try { + // Run through all libraries within the lib folder of the project and add them to the project. Removing all existing dependencies beforehand makes sure + // that we avoid double entries. And it avoids the problem with invalid dependencies + this.project.getDependencies().clear(); + final List<String> libraries = this.projectService.listAllLibraries(this.projectName); + + for (final String library : libraries) { + this.enrichProjectWithLibrary(library); + } + + } catch (final ProjectNotExistingException ex) { + throw new ProjectLoadException("The project does not exist.", ex); + } + } + + private void enrichProjectWithLibrary(final String fileName) { + final MIDependency mDependency = MODEL_FACTORY.createDependency(); + mDependency.setFilePath(fileName); + + this.project.getDependencies().add(mDependency); + } + + private void resetTimeStampFromLastSaving() { + this.timeStampFromLastSaving = System.currentTimeMillis(); + } + + /** + * This method sets the property {@link CurrentAnalysisEditorBean#unsavedModifications} to false and refreshes the necessary components within the analysis + * editor to make this visible. + */ + private void clearModificationsFlag() { + this.unsavedModifications = false; + RequestContext.getCurrentInstance().update("menuForm"); + } + + /** + * This method sets the property {@link CurrentAnalysisEditorBean#unsavedModifications} to true and refreshes the necessary components within the analysis editor + * to make this visible. + */ + private void setModificationsFlag() { + this.unsavedModifications = true; + RequestContext.getCurrentInstance().update("menuForm"); + } + + private void initializeGlobalConfigurationInstance() { + this.globalConfigurationInstance = MODEL_FACTORY.createFilter(); + + final kieker.analysis.annotation.AnalysisController annotation = AnalysisController.class.getAnnotation(kieker.analysis.annotation.AnalysisController.class); + final Property[] properties = annotation.configuration(); + final Map<String, String> propertyMap = new HashMap<String, String>(); + + for (final Property property : properties) { + propertyMap.put(property.name(), property.defaultValue()); + } + + for (final MIProperty mProperty : this.project.getProperties()) { + propertyMap.put(mProperty.getName(), mProperty.getValue()); + } + + for (final Map.Entry<String, String> property : propertyMap.entrySet()) { + final MIProperty mProperty = MODEL_FACTORY.createProperty(); + mProperty.setName(property.getKey()); + mProperty.setValue(property.getValue()); + + this.globalConfigurationInstance.getProperties().add(mProperty); + } } } diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java index fce9de8c..b1d76118 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java @@ -16,10 +16,6 @@ package kieker.webgui.web.beans.view; -import java.util.Map; - -import javax.faces.context.FacesContext; - import kieker.analysis.model.analysisMetaModel.MIAnalysisComponent; import kieker.analysis.model.analysisMetaModel.MIFilter; import kieker.analysis.model.analysisMetaModel.MIInputPort; @@ -46,8 +42,9 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; /** - * The {@link CurrentAnalysisEditorGraphBean} contains the necessary data behind an graph of the analysis editor. It provides various methods to manipulate the - * current graph.<br> + * This bean contains the necessary data behind an graph of the analysis editor. It provides various methods to manipulate the current graph.<br> + * </br> + * * The class is a Spring managed bean with view scope to make sure that one user (even in one session) can open multiple projects at a time without causing any * problems. * @@ -59,17 +56,20 @@ public class CurrentAnalysisEditorGraphBean { private static final Log LOG = LogFactory.getLog(CurrentAnalysisEditorGraphBean.class); - private static final String PARAM_NAME_ID = "ID"; - private static final String PARAM_NAME_NODES = "nodes"; - private static final String PARAM_NAME_EDGES = "edges"; - private static final String PARAM_NAME_TARGET_PORT_ID = "targetPortID"; - private static final String PARAM_NAME_SOURCE_PORT_ID = "sourcePortID"; + private static final String[] PARAMETER_NAMES_AUTO_LAYOUT = { "nodes", "edges" }; + private static final Class<?>[] PARAMETER_TYPES_AUTO_LAYOUT = { String.class, String.class }; + + private static final String[] PARAMETER_NAMES_ADD_AND_REMOVE_EDGES = { "sourcePortID", "targetPortID" }; + private static final Class<?>[] PARAMETER_TYPES_ADD_AND_REMOVE_EDGES = { String.class, String.class }; + + private static final String[] PARAMETER_NAMES_CLICK_AND_REMOVE_NODES = { "ID" }; + private static final Class<?>[] PARAMETER_TYPES_CLICK_AND_REMOVE_NODEST = { String.class }; /* * The following are a lot of code fragments which are used for the interaction with the flow editor. For details look at the API description of the flow editor * itself. */ - private static final String JS_CMD_CREATE_GRAPH_VAR = "var graph = GraphFlow()"; + private static final String JS_CMD_CREATE_GRAPH_VARIABLE = "var graph = GraphFlow()"; private static final String JS_CMD_ADD_EDGE_CONSTRAINTS = "graph.addEdgeConstraints()"; private static final String JS_CMD_SET_GLOBAL_ICON = "graph.setNodeIcon('Global', '../img/graphIcons/ProjectConfiguration.png', true)"; @@ -78,19 +78,28 @@ public class CurrentAnalysisEditorGraphBean { private static final String JS_CMD_SET_REPOSITORY_ICON = "graph.setNodeIcon('Repository', '../img/graphIcons/RepositoryIcon.png', true)"; private static final String JS_CMD_ADD_CLICK_NODE_LISTENER = "graph.addListener('onClick', nodeClickListener)"; + private static final String JS_CMD_ADD_ENTER_NODE_LISTENER = "graph.addListener('onMouseEnter', nodeEnterListener)"; private static final String JS_CMD_ADD_REMOVE_NODE_LISTENER = "graph.addListener('onRemoveNode', nodeRemoveListener)"; private static final String JS_CMD_ADD_CREATE_EDGE_LISTENER = "graph.addListener('onCreateEdge', edgeCreateListener)"; private static final String JS_CMD_ADD_REMOVE_EDGE_LISTENER = "graph.addListener('onRemoveEdge', edgeRemoveListener)"; private static final String JS_CMD_ADD_AUTO_LAYOUT_LISTENER = "graph.addListener('autoLayout', autoLayoutListener)"; - private static final String JS_CMD_NODE = "{'id':'%s', 'name':'%s', 'nodeClass':'%s', 'tooltip':'%s'}"; - private static final String JS_CMD_PORT = "{'name':'%s','id':'%s', 'tooltip':'%s'}"; + private static final String JS_TEMPLATE_NODE = "{'id':'%s', 'name':'%s', 'nodeClass':'%s', 'tooltip':'%s'}"; + private static final String JS_TEMPLATE_PORT = "{'name':'%s','id':'%s', 'tooltip':'%s'}"; + + private static final String JS_CMD_CREATE_GRAPH_CENTER_VARIABLE = "var center = graph.getScreenCenter()"; + + private static final Object REPOSITORY_INPUT_PORT = "R"; + private static final String JS_PORT_TYPE_INPUT = "inputPort"; - private static final String JS_CMD_PORT_TYPE_INPUT = "inputPort"; private static final String JS_CMD_ADD_EDGE = "graph.addEdge('%s', '%s', '%s', false, false)"; - private static final String JS_CMD_ADD_FILTER = "var center = graph.getScreenCenter(); graph.addNode(center.x, center.y, %s,[%s],[%s],[%s],'Filter', false)"; - private static final String JS_CMD_ADD_READER = "var center = graph.getScreenCenter(); graph.addNode(center.x, center.y, %s,[%s],null,[%s], 'Reader', false)"; - private static final String JS_CMD_ADD_REPOSITORY = "var center = graph.getScreenCenter(); graph.addNode(center.x, center.y, %s, null, [%s], null, 'Repository',false)"; - private static final String JS_CMD_ADD_GLOBAL_COMPONENT = "var center = graph.getScreenCenter(); graph.addNode(center.x, center.y, {'id':'%s', 'name':'%s', 'nodeClass':'', 'tooltip':'%s'}, null, null, null, 'Global', false); graph.setNodeData('%s.close', {'$visible' : false});"; + + private static final String JS_CMD_ADD_FILTER = JS_CMD_CREATE_GRAPH_CENTER_VARIABLE + "; graph.addNode(center.x,center.y,%s,[%s],[%s],[%s],'Filter',false)"; + private static final String JS_CMD_ADD_READER = JS_CMD_CREATE_GRAPH_CENTER_VARIABLE + "; graph.addNode(center.x,center.y,%s,[%s],null,[%s],'Reader',false)"; + private static final String JS_CMD_ADD_REPOSITORY = JS_CMD_CREATE_GRAPH_CENTER_VARIABLE + + "; graph.addNode(center.x,center.y, %s,null, [%s],null,'Repository',false)"; + private static final String JS_CMD_ADD_GLOBAL_COMPONENT = JS_CMD_CREATE_GRAPH_CENTER_VARIABLE + + "; graph.addNode(center.x,center.y,{'id':'%s','name':'%s','nodeClass':'','tooltip':'%s'}, null, null, null, 'Global', false)" + + "; graph.setNodeData('%s.close', {'$visible' : false});"; private static final String JS_CMD_ENABLE_GRID = "graph.setGridVisible(true, false)"; private static final String JS_CMD_DISABLE_GRID = "graph.setGridVisible(false, false)"; @@ -105,10 +114,8 @@ public class CurrentAnalysisEditorGraphBean { private static final String JS_CMD_LOAD_FROM_LAYOUT = "graph.loadPositions('%s')"; private static final String JS_CMD_RENAME_NODE = "graph.setNodeData('%s', {'name' : '%s'})"; - private static final Object REPOSITORY_INPUT_PORT = "R"; - private final Registry<MIRepositoryConnector> repositoryPortMap = new Registry<MIRepositoryConnector>(); - private final Registry<MIPort> portMap = new Registry<MIPort>(); + private final Registry<MIPort> filterPortMap = new Registry<MIPort>(); private final Registry<MIAnalysisComponent> componentMap = new Registry<MIAnalysisComponent>(); private CurrentAnalysisEditorBean currentAnalysisEditorBean; @@ -133,69 +140,46 @@ public class CurrentAnalysisEditorGraphBean { * Declares the the graph variable. */ public void declareGraph() { - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_CREATE_GRAPH_VAR); + RequestContext.getCurrentInstance().execute(JS_CMD_CREATE_GRAPH_VARIABLE); - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_SET_FILTER_ICON); - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_SET_REPOSITORY_ICON); - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_SET_READER_ICON); - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_SET_GLOBAL_ICON); - - // Create an MIAnalysisComponent instance, which represents the global configuration of the project - final MIAnalysisComponent globalComponent = this.currentAnalysisEditorBean.getGlobalConfigurationComponent(); - final int id = this.componentMap.get(globalComponent); - RequestContext.getCurrentInstance() - .execute( - String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_GLOBAL_COMPONENT, "id" + id, - this.globalPropertiesBean.getGlobalConfigurationComponentName(), this.globalPropertiesBean.getGlobalConfigurationComponentName(), - "id" + id)); + RequestContext.getCurrentInstance().execute(JS_CMD_SET_FILTER_ICON); + RequestContext.getCurrentInstance().execute(JS_CMD_SET_REPOSITORY_ICON); + RequestContext.getCurrentInstance().execute(JS_CMD_SET_READER_ICON); + RequestContext.getCurrentInstance().execute(JS_CMD_SET_GLOBAL_ICON); } /** * Initializes the listeners for the graph. */ public void initListeners() { - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_CLICK_NODE_LISTENER); - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_AUTO_LAYOUT_LISTENER); - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_REMOVE_NODE_LISTENER); - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_CREATE_EDGE_LISTENER); - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_REMOVE_EDGE_LISTENER); + RequestContext.getCurrentInstance().execute(JS_CMD_ADD_CLICK_NODE_LISTENER); + RequestContext.getCurrentInstance().execute(JS_CMD_ADD_ENTER_NODE_LISTENER); - RequestContext.getCurrentInstance().execute("graph.addListener('onMouseEnter', function() {graph.setMouseCursor('pointer')})"); - } + RequestContext.getCurrentInstance().execute(JS_CMD_ADD_AUTO_LAYOUT_LISTENER); - /** - * This method initializes the auto layout of the graph, resulting in a javascript event, delivering the necessary information about the graph. - */ - public void startAutoLayout() { - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_START_AUTO_LAYOUT); + RequestContext.getCurrentInstance().execute(JS_CMD_ADD_REMOVE_NODE_LISTENER); + RequestContext.getCurrentInstance().execute(JS_CMD_ADD_CREATE_EDGE_LISTENER); + RequestContext.getCurrentInstance().execute(JS_CMD_ADD_REMOVE_EDGE_LISTENER); } /** - * This method uses the given parameters (via request parameter map) to calculate a new layout for the graph and use this layout. + * Adds the edge constraints to the graph. */ - public void autoLayout() { - try { - // Get the parameters - final Map<String, String> paramMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); - - final String nodes = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_NODES); - final String edges = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_EDGES); - - // Calculate the layout - final String newLayout = this.layouter.layoutGraph(nodes, edges); - - // Now use it - RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_LOAD_FROM_LAYOUT, newLayout)); - } catch (final GraphLayoutException ex) { - CurrentAnalysisEditorGraphBean.LOG.warn("Autolayout failed.", ex); - } + public void initEdgeConstraints() { + RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_EDGE_CONSTRAINTS); } /** - * Adds the edge constraints to the graph. + * Adds a non removable component to the graph, which can be used to edit project configurations. + * + * @param globalConfigurationInstance + * The global configuration instance. */ - public void addEdgeConstraints() { - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_EDGE_CONSTRAINTS); + public void addGlobalConfigurationInstance(final MIAnalysisComponent globalConfigurationInstance) { + final String name = this.globalPropertiesBean.getGlobalConfigurationComponentName(); + final int id = this.componentMap.get(globalConfigurationInstance); + + RequestContext.getCurrentInstance().execute(String.format(JS_CMD_ADD_GLOBAL_COMPONENT, "id" + id, name, name, "id" + id)); } /** @@ -205,7 +189,7 @@ public class CurrentAnalysisEditorGraphBean { * The filter which should be added to the graph. */ public void addFilter(final MIFilter filter) { - RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_FILTER, this.assembleGraphString(filter), + RequestContext.getCurrentInstance().execute(String.format(JS_CMD_ADD_FILTER, this.assembleGraphString(filter), this.assembleGraphRepositoryPortString(filter.getRepositories()), this.assembleGraphInputPortString(filter), this.assembleGraphOutputPortString(filter))); } @@ -217,7 +201,7 @@ public class CurrentAnalysisEditorGraphBean { * The reader which should be added to the graph. */ public void addReader(final MIReader reader) { - RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_READER, this.assembleGraphString(reader), + RequestContext.getCurrentInstance().execute(String.format(JS_CMD_ADD_READER, this.assembleGraphString(reader), this.assembleGraphRepositoryPortString(reader.getRepositories()), this.assembleGraphOutputPortString(reader))); } @@ -228,10 +212,33 @@ public class CurrentAnalysisEditorGraphBean { * The repository which should be added to the graph. */ public void addRepository(final MIRepository repository) { - final String repoPort = String.format(CurrentAnalysisEditorGraphBean.JS_CMD_PORT, CurrentAnalysisEditorGraphBean.JS_CMD_PORT_TYPE_INPUT, - CurrentAnalysisEditorGraphBean.REPOSITORY_INPUT_PORT, "N/A"); - RequestContext.getCurrentInstance().execute( - String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_REPOSITORY, this.assembleGraphString(repository), repoPort)); + final String repoPort = String.format(JS_TEMPLATE_PORT, JS_PORT_TYPE_INPUT, REPOSITORY_INPUT_PORT, "N/A"); + RequestContext.getCurrentInstance().execute(String.format(JS_CMD_ADD_REPOSITORY, this.assembleGraphString(repository), repoPort)); + } + + /** + * This method initializes the auto layout of the graph, resulting in a JavaScript event, delivering the necessary information about the graph. + */ + public void startAutoLayout() { + RequestContext.getCurrentInstance().execute(JS_CMD_START_AUTO_LAYOUT); + } + + /** + * This method uses the given parameters (via request parameter map) to calculate a new layout for the graph and use this layout. + */ + public void autoLayout() { + try { + // Get the parameters + final Object[] parameters = GlobalPropertiesBean.convertObjectsFromParameterMap(PARAMETER_NAMES_AUTO_LAYOUT, PARAMETER_TYPES_AUTO_LAYOUT); + final String nodes = (String) parameters[0]; + final String edges = (String) parameters[1]; + + // Calculate the layout and use it + final String newLayout = this.layouter.layoutGraph(nodes, edges); + RequestContext.getCurrentInstance().execute(String.format(JS_CMD_LOAD_FROM_LAYOUT, newLayout)); + } catch (final GraphLayoutException ex) { + CurrentAnalysisEditorGraphBean.LOG.warn("Autolayout failed.", ex); + } } /** @@ -247,7 +254,7 @@ public class CurrentAnalysisEditorGraphBean { * The input port. */ public void addConnection(final MIPlugin source, final MIPlugin destination, final MIOutputPort outputPort, final MIInputPort inputPort) { - RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_EDGE, this.assembleGraphPortID(source, outputPort), + RequestContext.getCurrentInstance().execute(String.format(JS_CMD_ADD_EDGE, this.assembleGraphPortID(source, outputPort), this.assembleGraphPortID(destination, inputPort), "")); } @@ -262,145 +269,19 @@ public class CurrentAnalysisEditorGraphBean { * The repository port. */ public void addConnection(final MIPlugin source, final MIRepository destination, final MIRepositoryConnector port) { - RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_ADD_EDGE, this.assembleGraphPortID(source, port), + RequestContext.getCurrentInstance().execute(String.format(JS_CMD_ADD_EDGE, this.assembleGraphPortID(source, port), this.assembleGraphPortID(destination), "")); } - /** - * This method assembles the string containing the given ports. - * - * @param ports - * The ports to be used within the string command. - * @return A string containing the JS commands to create the ports. - */ - private String assembleGraphPortString(final EList<? extends MIPort> ports) { - final StringBuilder builder = new StringBuilder(); - final int len = ports.size(); - - for (int i = 0; i < len; i++) { - final MIPort port = ports.get(i); - - if (i != 0) { - builder.append(','); - } - - builder.append(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_PORT, CurrentAnalysisEditorGraphBean.simpleEscape(port.getName()), - this.portMap.get(port), CurrentAnalysisEditorGraphBean.simpleEscape(port.getName()))); - } - - return builder.toString(); - - } - - /** - * This method assembles the string containing the available repository ports of the given plugin. - * - * @param ports - * The ports which will be used. - * @return A string containing the JS commands to create the repository ports. - */ - private String assembleGraphRepositoryPortString(final EList<MIRepositoryConnector> ports) { - final StringBuilder builder = new StringBuilder(); - final int len = ports.size(); - - for (int i = 0; i < len; i++) { - final MIRepositoryConnector port = ports.get(i); - - if (i != 0) { - builder.append(','); - } - - builder.append(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_PORT, CurrentAnalysisEditorGraphBean.simpleEscape(port.getName()), - this.repositoryPortMap.get(port), CurrentAnalysisEditorGraphBean.simpleEscape(port.getName()))); - } - - return builder.toString(); - } - - /** - * This method assembles the string containing the available input ports of the given filter. - * - * @param filter - * The filter whose input ports will be used. - * @return A string containing the JS commands to create the input ports. - */ - private String assembleGraphInputPortString(final MIFilter filter) { - return this.assembleGraphPortString(filter.getInputPorts()); - } - - /** - * This method assembles the string containing the available output ports of the given plugin. - * - * @param plugin - * The plugin whose output ports will be used. - * @return A string containing the JS commands to create the output ports. - */ - private String assembleGraphOutputPortString(final MIPlugin plugin) { - return this.assembleGraphPortString(plugin.getOutputPorts()); - } - - /** - * This method assembles the ID of the port for the graph based on the given parameters. - * - * @param plugin - * The parent plugin of the port. - * @param port - * The port itself. - * @return The ID for the port within the graph - */ - private String assembleGraphPortID(final MIPlugin plugin, final MIPort port) { - return "id" + this.componentMap.get(plugin) + "." + this.portMap.get(port); - } - - /** - * This method assembles the ID of the port for the graph based on the given parameters. - * - * @param plugin - * The parent plugin of the port. - * @param port - * The port itself. - * @return The ID for the port within the graph - */ - private Object assembleGraphPortID(final MIPlugin plugin, final MIRepositoryConnector port) { - return "id" + this.componentMap.get(plugin) + "." + this.repositoryPortMap.get(port); - } - - /** - * This method assembles the ID of the port for the graph based on the given parameters. - * - * @param repository - * The parent repository of the port. - * @return The ID for the port within the graph - */ - private Object assembleGraphPortID(final MIRepository repository) { - return "id" + this.componentMap.get(repository) + "." + CurrentAnalysisEditorGraphBean.REPOSITORY_INPUT_PORT; - } - - /** - * Assembles a human-readable string of the given repository or plugin, which can also be used as a graph ID. - * - * @param component - * The component whose ID should be delivered. - * @return A human readable ID. - */ - private String assembleGraphString(final MIAnalysisComponent component) { - final String name = component.getName(); - final String className = component.getClassname(); - final String shortName = className.substring(className.lastIndexOf('.') + 1); - - return String.format(CurrentAnalysisEditorGraphBean.JS_CMD_NODE, "id" + this.componentMap.get(component), - CurrentAnalysisEditorGraphBean.simpleEscape(name), CurrentAnalysisEditorGraphBean.simpleEscape(shortName), - CurrentAnalysisEditorGraphBean.simpleEscape(className)); - } - /** * This is the action which can be called from the javascript code to show that a node has been clicked. It informs the connected * {@link CurrentAnalysisEditorBean} about this. */ public void nodeClicked() { // Get the parameters - final Map<String, String> paramMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); - final String clickedNodeID = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_ID); + final Object[] parameters = GlobalPropertiesBean.convertObjectsFromParameterMap(PARAMETER_NAMES_CLICK_AND_REMOVE_NODES, + PARAMETER_TYPES_CLICK_AND_REMOVE_NODEST); + final String clickedNodeID = (String) parameters[0]; if (clickedNodeID.isEmpty()) { // Deselect @@ -425,8 +306,11 @@ public class CurrentAnalysisEditorGraphBean { */ public void nodeRemoved() { // Get the parameters - final Map<String, String> paramMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); - final String clickedNodeID = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_ID); + + final Object[] parameters = GlobalPropertiesBean.convertObjectsFromParameterMap(PARAMETER_NAMES_CLICK_AND_REMOVE_NODES, + PARAMETER_TYPES_CLICK_AND_REMOVE_NODEST); + final String clickedNodeID = (String) parameters[0]; + // Now search the correct node try { final MIAnalysisComponent selectedNode = this.componentMap.get(Integer.parseInt(clickedNodeID.substring(2))); @@ -445,26 +329,24 @@ public class CurrentAnalysisEditorGraphBean { */ public void edgeCreated() { // Get the parameters - final Map<String, String> paramMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); - - final String sourcePortID = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_SOURCE_PORT_ID).split("\\.")[1]; - final String targetPortID = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_TARGET_PORT_ID).split("\\.")[1]; + final Object[] parameters = GlobalPropertiesBean.convertObjectsFromParameterMap(PARAMETER_NAMES_ADD_AND_REMOVE_EDGES, PARAMETER_TYPES_ADD_AND_REMOVE_EDGES); + final String sourcePortID = ((String) parameters[0]).split("\\.")[1]; + final String targetPortID = ((String) parameters[1]).split("\\.")[1]; // Now search the correct components try { - if (CurrentAnalysisEditorGraphBean.REPOSITORY_INPUT_PORT.equals(targetPortID)) { + if (REPOSITORY_INPUT_PORT.equals(targetPortID)) { // This is a special case: An edge between a filter and a repository final MIRepositoryConnector sourcePort = this.repositoryPortMap.get(Integer.parseInt(sourcePortID)); - final String targetID = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_TARGET_PORT_ID).split("\\.")[0]; + final String targetID = ((String) parameters[1]).split("\\.")[0]; final MIAnalysisComponent targetRepo = this.componentMap.get(Integer.parseInt(targetID.substring(2))); - if ((sourcePort != null) && (targetRepo instanceof MIRepository) && (this.currentAnalysisEditorBean != null)) { this.currentAnalysisEditorBean.edgeCreated(sourcePort, (MIRepository) targetRepo); } } else { // This is the normal case: An edge between two filters - final MIPort sourcePort = this.portMap.get(Integer.parseInt(sourcePortID)); - final MIPort targetPort = this.portMap.get(Integer.parseInt(targetPortID)); + final MIPort sourcePort = this.filterPortMap.get(Integer.parseInt(sourcePortID)); + final MIPort targetPort = this.filterPortMap.get(Integer.parseInt(targetPortID)); if ((sourcePort != null) && (targetPort != null) && (this.currentAnalysisEditorBean != null)) { this.currentAnalysisEditorBean.edgeCreated((MIOutputPort) sourcePort, (MIInputPort) targetPort); @@ -482,25 +364,24 @@ public class CurrentAnalysisEditorGraphBean { */ public void edgeRemoved() { // Get the parameters - final Map<String, String> paramMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap(); - - final String sourcePortID = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_SOURCE_PORT_ID).split("\\.")[1]; - final String targetPortID = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_TARGET_PORT_ID).split("\\.")[1]; + final Object[] parameters = GlobalPropertiesBean.convertObjectsFromParameterMap(PARAMETER_NAMES_ADD_AND_REMOVE_EDGES, PARAMETER_TYPES_ADD_AND_REMOVE_EDGES); + final String sourcePortID = ((String) parameters[0]).split("\\.")[1]; + final String targetPortID = ((String) parameters[1]).split("\\.")[1]; // Now search the correct components try { if (CurrentAnalysisEditorGraphBean.REPOSITORY_INPUT_PORT.equals(targetPortID)) { // This is a special case: An edge between a filter and a repository final MIRepositoryConnector sourcePort = this.repositoryPortMap.get(Integer.parseInt(sourcePortID)); - final String targetID = paramMap.get(CurrentAnalysisEditorGraphBean.PARAM_NAME_TARGET_PORT_ID).split("\\.")[0]; + final String targetID = ((String) parameters[1]).split("\\.")[0]; final MIAnalysisComponent targetRepo = this.componentMap.get(Integer.parseInt(targetID.substring(2))); if ((sourcePort != null) && (targetRepo instanceof MIRepository) && (this.currentAnalysisEditorBean != null)) { this.currentAnalysisEditorBean.edgeRemoved(sourcePort, (MIRepository) targetRepo); } } else { // This is the normal case: An edge between two filters - final MIPort sourcePort = this.portMap.get(Integer.parseInt(sourcePortID)); - final MIPort targetPort = this.portMap.get(Integer.parseInt(targetPortID)); + final MIPort sourcePort = this.filterPortMap.get(Integer.parseInt(sourcePortID)); + final MIPort targetPort = this.filterPortMap.get(Integer.parseInt(targetPortID)); if ((sourcePort != null) && (targetPort != null) && (this.currentAnalysisEditorBean != null)) { this.currentAnalysisEditorBean.edgeRemoved((MIOutputPort) sourcePort, (MIInputPort) targetPort); @@ -586,22 +467,7 @@ public class CurrentAnalysisEditorGraphBean { */ public void checkReadOnlyForGuests() { if ("Guest".equals(this.userBean.getUserrole())) { - RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_SET_READ_ONLY); - } - } - - /** - * This method escapes the given string by removing the character ' from the string. This is necessary as the character could destroy the flow editor. - * - * @param str - * The string to be escaped. - * @return The escaped string. - */ - private static final String simpleEscape(final String str) { - if (str != null) { - return str.replaceAll("\'", ""); - } else { - return str; + RequestContext.getCurrentInstance().execute(JS_CMD_SET_READ_ONLY); } } @@ -624,7 +490,149 @@ public class CurrentAnalysisEditorGraphBean { * The new layout for the graph. */ public void loadLayout(final String layout) { - RequestContext.getCurrentInstance().execute(String.format(CurrentAnalysisEditorGraphBean.JS_CMD_LOAD_FROM_LAYOUT, layout)); + RequestContext.getCurrentInstance().execute(String.format(JS_CMD_LOAD_FROM_LAYOUT, layout)); + } + + /** + * This method assembles the string containing the given ports. + * + * @param ports + * The ports to be used within the string command. + * @return A string containing the JS commands to create the ports. + */ + private String assembleGraphPortString(final EList<? extends MIPort> ports) { + final StringBuilder builder = new StringBuilder(); + final int len = ports.size(); + + for (int i = 0; i < len; i++) { + final MIPort port = ports.get(i); + + if (i != 0) { + builder.append(','); + } + + builder.append(String.format(JS_TEMPLATE_PORT, CurrentAnalysisEditorGraphBean.simpleEscape(port.getName()), this.filterPortMap.get(port), + CurrentAnalysisEditorGraphBean.simpleEscape(port.getName()))); + } + + return builder.toString(); + + } + + /** + * This method assembles the string containing the available repository ports of the given plugin. + * + * @param ports + * The ports which will be used. + * @return A string containing the JS commands to create the repository ports. + */ + private String assembleGraphRepositoryPortString(final EList<MIRepositoryConnector> ports) { + final StringBuilder builder = new StringBuilder(); + final int len = ports.size(); + + for (int i = 0; i < len; i++) { + final MIRepositoryConnector port = ports.get(i); + + if (i != 0) { + builder.append(','); + } + + builder.append(String.format(JS_TEMPLATE_PORT, CurrentAnalysisEditorGraphBean.simpleEscape(port.getName()), this.repositoryPortMap.get(port), + CurrentAnalysisEditorGraphBean.simpleEscape(port.getName()))); + } + + return builder.toString(); + } + + /** + * This method assembles the string containing the available input ports of the given filter. + * + * @param filter + * The filter whose input ports will be used. + * @return A string containing the JS commands to create the input ports. + */ + private String assembleGraphInputPortString(final MIFilter filter) { + return this.assembleGraphPortString(filter.getInputPorts()); + } + + /** + * This method assembles the string containing the available output ports of the given plugin. + * + * @param plugin + * The plugin whose output ports will be used. + * @return A string containing the JS commands to create the output ports. + */ + private String assembleGraphOutputPortString(final MIPlugin plugin) { + return this.assembleGraphPortString(plugin.getOutputPorts()); + } + + /** + * This method assembles the ID of the port for the graph based on the given parameters. + * + * @param plugin + * The parent plugin of the port. + * @param port + * The port itself. + * @return The ID for the port within the graph + */ + private String assembleGraphPortID(final MIPlugin plugin, final MIPort port) { + return "id" + this.componentMap.get(plugin) + "." + this.filterPortMap.get(port); + } + + /** + * This method assembles the ID of the port for the graph based on the given parameters. + * + * @param plugin + * The parent plugin of the port. + * @param port + * The port itself. + * @return The ID for the port within the graph + */ + private Object assembleGraphPortID(final MIPlugin plugin, final MIRepositoryConnector port) { + return "id" + this.componentMap.get(plugin) + "." + this.repositoryPortMap.get(port); + } + + /** + * This method assembles the ID of the port for the graph based on the given parameters. + * + * @param repository + * The parent repository of the port. + * @return The ID for the port within the graph + */ + private Object assembleGraphPortID(final MIRepository repository) { + return "id" + this.componentMap.get(repository) + "." + REPOSITORY_INPUT_PORT; + } + + /** + * Assembles a human-readable string of the given repository or plugin, which can also be used as a graph ID. + * + * @param component + * The component whose ID should be delivered. + * @return A human readable ID. + */ + private String assembleGraphString(final MIAnalysisComponent component) { + final String name = component.getName(); + final String className = component.getClassname(); + final String shortName = className.substring(className.lastIndexOf('.') + 1); + + return String.format(JS_TEMPLATE_NODE, "id" + this.componentMap.get(component), + CurrentAnalysisEditorGraphBean.simpleEscape(name), CurrentAnalysisEditorGraphBean.simpleEscape(shortName), + CurrentAnalysisEditorGraphBean.simpleEscape(className)); + } + + /** + * This method escapes the given string by removing the character ' from the string. This is necessary as the character could destroy the flow editor. + * + * @param str + * The string to be escaped. + * @return The escaped string. + */ + private static final String simpleEscape(final String str) { + if (str != null) { + return str.replaceAll("\'", ""); + } else { + return str; + } } } diff --git a/Kieker.WebGUI/src/main/webapp/dialogs/AnalysisEditorPageDialogs.xhtml b/Kieker.WebGUI/src/main/webapp/dialogs/AnalysisEditorPageDialogs.xhtml index e135e206..51c82964 100644 --- a/Kieker.WebGUI/src/main/webapp/dialogs/AnalysisEditorPageDialogs.xhtml +++ b/Kieker.WebGUI/src/main/webapp/dialogs/AnalysisEditorPageDialogs.xhtml @@ -30,7 +30,7 @@ <br /> <br /> <h:form enctype="multipart/form-data"> - <p:fileUpload label="#{localizedMessages.choose}" cancelLabel="#{localizedMessages.cancel}" auto="true" allowTypes="/(\.|\/)(jar)$/" sizeLimit="104857600" mode="advanced" fileUploadListener="#{currentAnalysisEditorBean.handleFileUpload}" update=":dependenciesForm :messages :toolpalette"/> + <p:fileUpload label="#{localizedMessages.choose}" cancelLabel="#{localizedMessages.cancel}" auto="true" allowTypes="/(\.|\/)(jar)$/" sizeLimit="104857600" mode="advanced" fileUploadListener="#{currentAnalysisEditorBean.handleLibraryFileUpload}" update=":dependenciesForm :messages :toolpalette"/> </h:form> </div> <hr/> diff --git a/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml index 60dc1452..b76f59c8 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/AnalysisEditorPage.xhtml @@ -39,7 +39,11 @@ </ui:define> <ui:define name="js"> - <script> + <script> + nodeEnterListener = function() { + graph.setMouseCursor('pointer'); + } + nodeClickListener = function(node, info, e) { nodeClickCommand([{name : 'ID', value : node.id}]); markNode(node, '#FF0000'); -- GitLab