From 47da46835f40883cd576487d7b55302af164ac46 Mon Sep 17 00:00:00 2001
From: Nils Christian Ehmke <nie@informatik.uni-kiel.de>
Date: Sun, 16 Sep 2012 19:48:00 +0200
Subject: [PATCH] Modified the cockpit editor

---
 .../beans/view/CurrentCockpitEditorBean.java  | 73 ++++++++++++++++
 .../common/ClassAndMethodContainer.java       | 11 +++
 .../src/main/webapp/CockpitEditor.xhtml       | 84 +++++++++++--------
 .../src/main/webapp/dialogs/viewDialogs.xhtml |  2 +-
 4 files changed, 134 insertions(+), 36 deletions(-)

diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitEditorBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitEditorBean.java
index 178a1188..da75e0d5 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitEditorBean.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/beans/view/CurrentCockpitEditorBean.java
@@ -21,6 +21,7 @@
 package kieker.webgui.beans.view;
 
 import java.io.IOException;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
@@ -46,6 +47,7 @@ import kieker.analysis.plugin.AbstractPlugin;
 import kieker.common.logging.Log;
 import kieker.common.logging.LogFactory;
 import kieker.webgui.beans.application.ProjectsBean;
+import kieker.webgui.common.ClassAndMethodContainer;
 import kieker.webgui.common.IProjectManagerFacade;
 import kieker.webgui.common.ProjectManagerFacade;
 import kieker.webgui.common.exception.NewerProjectException;
@@ -93,6 +95,8 @@ public class CurrentCockpitEditorBean {
 	@ManagedProperty(value = "#{projectsBean}")
 	private ProjectsBean projectsBean;
 	private final IProjectManagerFacade projectManagerFacade = ProjectManagerFacade.getInstance();
+	private ClassAndMethodContainer classAndMethodContainer;
+	private ClassLoader classLoader;
 
 	/**
 	 * Creates a new instance of this class. <b>Do not call this constructor manually. It will only be accessed by JSF.</b>
@@ -134,6 +138,9 @@ public class CurrentCockpitEditorBean {
 					if (this.project != null) {
 						// Remember the current time! This is important for the later comparison of the time stamps.
 						this.resetTimeStamp();
+						// Update the class loader and the specific classes used within various methods in this bean
+						this.reloadClassLoader();
+						this.reloadClassesAndMethods();
 					}
 				}
 			} catch (final ProjectLoadException ex) {
@@ -143,6 +150,39 @@ public class CurrentCockpitEditorBean {
 		}
 	}
 
+	/**
+	 * This method reloads the field containing the methods and classes, using the current class loader.
+	 * 
+	 * @throws ProjectLoadException
+	 *             If one or more of the classes and methods necessary for {@link ClassAndMethodContainer} could not be found using the given class loader.
+	 */
+	private void reloadClassesAndMethods() throws ProjectLoadException {
+		synchronized (this) {
+			this.classAndMethodContainer = new ClassAndMethodContainer(this.classLoader);
+		}
+	}
+
+	/**
+	 * This method reloads the class loader. In other words: The class loader will always be able to load classes from the jar-files within the lib-folder of the
+	 * project.
+	 * 
+	 * @throws ProjectLoadException
+	 *             If something went wrong.
+	 */
+	private void reloadClassLoader() throws ProjectLoadException {
+		synchronized (this) {
+			try {
+				this.classLoader = this.projectManagerFacade.getClassLoader(this.projectName); // NOPMD (ClassLoader)
+			} catch (final NullPointerException ex) {
+				throw new ProjectLoadException("Invalid class loader.", ex);
+			} catch (final IOException ex) {
+				throw new ProjectLoadException("Could not load classes.", ex);
+			} catch (final ProjectNotExistingException ex) {
+				throw new ProjectLoadException("Project does not exist.", ex);
+			}
+		}
+	}
+
 	/**
 	 * This method delivers the project stored in this bean.
 	 * 
@@ -177,6 +217,39 @@ public class CurrentCockpitEditorBean {
 		}
 	}
 
+	/**
+	 * This method can be used to get the description of a {@link Display}. The description is read via the annotation using the java reflection API.
+	 * 
+	 * @param display
+	 *            The display whose description should be extracted.
+	 * @return The description for the display or a substitute if none is available. This is in either case human readable.
+	 */
+	public String getDescription(final MIDisplay display) {
+		try {
+			final String classname = display.getParent().getClassname();
+			final String displayName = display.getName();
+			// Try to get the class
+			final Class<?> clazz = this.classLoader.loadClass(classname);
+			// Run through all methods of the class
+			for (final Method method : clazz.getMethods()) {
+				// Get the potential annotation
+				final Annotation annotationDisplay = method.getAnnotation(this.classAndMethodContainer.getDisplayAnnotationClass());
+				if (annotationDisplay != null) {
+					final String potDisplayName = (String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getDisplayNameMethod(),
+							annotationDisplay,
+							"");
+					if (potDisplayName.equals(displayName)) {
+						return (String) ClassAndMethodContainer.invokeMethod(this.classAndMethodContainer.getDisplayDescriptionMethod(), annotationDisplay,
+								"No description available");
+					}
+				}
+			}
+		} catch (final ClassNotFoundException ex) {
+			CurrentCockpitEditorBean.LOG.warn("Class not found.", ex);
+		}
+		return "No description available";
+	}
+
 	/**
 	 * This method tries to save the current project and informs the user about success or fail.
 	 * 
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java
index d1c0410c..ed6d3840 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/ClassAndMethodContainer.java
@@ -223,6 +223,7 @@ public final class ClassAndMethodContainer {
 	 * This is the loadFromFile(File)-method of the class equivalence of {@link AnalysisController}.
 	 */
 	private final Method analysisControllerLoadFromFile;
+	private Method displayDescriptionMethod;
 	/**
 	 * This is the constructor for {@link AnalysisControllerThread}, which gets an instance of {@link AnalysisController}.
 	 */
@@ -278,6 +279,7 @@ public final class ClassAndMethodContainer {
 			this.analysisControllerThreadTerminate = this.analysisControllerThreadClass.getMethod("terminate", new Class<?>[0]);
 			this.analysisControllerGetState = this.analysisControllerClass.getMethod("getState", new Class<?>[0]);
 			this.propertyDescriptionMethod = this.propertyAnnotationClass.getMethod("description", new Class<?>[0]);
+			this.displayDescriptionMethod = this.displayAnnotationClass.getMethod("description", new Class<?>[0]);
 
 			// This is a special case as we need to load some additional classes to search for the correct method
 			final Class<?> miProjectClass = classLoader.loadClass(MIProject.class.getName());
@@ -671,6 +673,15 @@ public final class ClassAndMethodContainer {
 		return this.analysisControllerLoadFromFile;
 	}
 
+	/**
+	 * The getter-method for the field {@link ClassAndMethodContainer#displayDescriptionMethod}.
+	 * 
+	 * @return The current value for the field.
+	 */
+	public Method getDisplayDescriptionMethod() {
+		return this.displayDescriptionMethod;
+	}
+
 	/**
 	 * This method can be used to invoke a given method with given parameters, without having to mind about the exceptions. If an exception occurs, the given default
 	 * value will be returned.
diff --git a/Kieker.WebGUI/src/main/webapp/CockpitEditor.xhtml b/Kieker.WebGUI/src/main/webapp/CockpitEditor.xhtml
index e3656385..d79976f5 100644
--- a/Kieker.WebGUI/src/main/webapp/CockpitEditor.xhtml
+++ b/Kieker.WebGUI/src/main/webapp/CockpitEditor.xhtml
@@ -7,11 +7,11 @@
       xmlns:p="http://primefaces.org/ui"
       xmlns:c="http://java.sun.com/jsp/jstl/core">
 
-     <f:metadata>
-         <f:viewParam name="projectName" value="#{currentCockpitEditorBean.projectName}"/>
-         <f:event type="preRenderView" listener="#{currentCockpitEditorBean.initalize()}"  />
+    <f:metadata>
+        <f:viewParam name="projectName" value="#{currentCockpitEditorBean.projectName}"/>
+        <f:event type="preRenderView" listener="#{currentCockpitEditorBean.initalize()}"  />
     </f:metadata>
-    
+
     <h:head>
         <title>Kieker.WebGUI</title>
         <link rel="stylesheet" type="text/css" href="../css/Common.css" />
@@ -70,12 +70,27 @@
                 </h:form>
             </p:layoutUnit>
 
-            <p:layoutUnit size="300" position="west">
-                <h:form rendered="#{not empty currentCockpitEditorBean.project}">
-                    <p:accordionPanel activeIndex="-1" value="#{currentCockpitEditorBean.project.plugins}" var="plugin">
-                        <p:tab title="#{plugin.name}">
-                            <ui:repeat value="#{plugin.displays}" var="display">
-                                <p:commandLink id="displayLink" value="#{display.name}" action="#{currentCockpitEditorBean.addDisplayToView(display)}" update=":viewsForm"/>
+            <p:layoutUnit position="west" resizable="true" size="300" collapsible="true" header="Available Views">
+                <h:form id="availableViewsForm">
+                    <p:dataTable value="#{currentCockpitEditorBean.project.views}" var="viewElem">
+                        <p:column headerText="View Name" style="font-weight: #{currentCockpitEditorBean.activeView == viewElem ? 'bold' : 'normal'}">
+                            <p:commandLink value="#{viewElem.name}" action="#{currentCockpitEditorBean.setActiveView(viewElem)}" update=":availableViewsForm :centerForm"/>
+                        </p:column>
+                        <p:column headerText="# Displays">
+                            #{viewElem.displayConnectors.size()}
+                        </p:column>
+                    </p:dataTable>
+                </h:form>
+            </p:layoutUnit>
+
+            <p:layoutUnit position="east" resizable="true" size="300" collapsible="true" header="Available Displays">
+                <h:form  rendered="#{not empty currentCockpitEditorBean.project}">
+                    <p:accordionPanel value="#{currentCockpitEditorBean.project.plugins}" multiple="true" activeIndex="" var="plugin">
+                        <p:tab title="#{plugin.name}" titletip="#{plugin.classname}">
+                            <h:outputText value="No Displays Available" rendered="#{empty plugin.displays}"/>
+                            <ui:repeat value="#{plugin.displays}" var="display">             
+                                <p:commandLink id="displayLink" value="#{display.name}" action="#{currentCockpitEditorBean.addDisplayToView(display)}" update=":availableViewsForm :centerForm"/><br/>
+                                <p:tooltip for="displayLink" value="#{currentCockpitEditorBean.getDescription(display)}"/>
                             </ui:repeat>
                         </p:tab>
                     </p:accordionPanel>
@@ -83,33 +98,32 @@
             </p:layoutUnit>
 
             <p:layoutUnit position="center">
-                <h:form id="viewsForm">
-                    <p:accordionPanel value="#{currentCockpitEditorBean.project.views}" var="viewComp" activeIndex="-1">
-                        <p:tab title="#{viewComp.name}" closable="true">
-                            <h:outputText value="Description: " />  
+                <h:form id="centerForm">
+                    <h:outputText value="Number of Columns: "/><br/>
+                    <h:outputText value="Description: " />  
+                    <p:inplace id="basic" editor="true">  
+                        <p:inputText value="#{currentCockpitEditorBean.activeView.description}" />  
+                    </p:inplace>  
+                    <br/>
+                    <hr/>
+                    <p:dataTable value="#{currentCockpitEditorBean.activeView.displayConnectors}" var="dispConn">
+                        <p:column headerText="Plugin" style="text-align: center">
+                            #{dispConn.display.parent.name}
+                        </p:column>
+                        <p:column headerText="Display Name" style="text-align: center">
+                            #{dispConn.display.name}
+                        </p:column>
+                        <p:column headerText="Name" style="text-align: center">
                             <p:inplace id="basic" editor="true">  
-                                <p:inputText value="#{viewComp.description}" />  
+                                <p:inputText value="#{dispConn.name}" validator="#{currentCockpitEditorBean.validateDisplayConnectorName}" />
                             </p:inplace>  
-                            <hr/>
-                            <p:dataTable value="#{viewComp.displayConnectors}" var="dispConn">
-                                <p:column headerText="Plugin" style="text-align: center">
-                                    #{dispConn.display.parent.name}
-                                </p:column>
-                                <p:column headerText="Display Name" style="text-align: center">
-                                    #{dispConn.display.name}
-                                </p:column>
-                                <p:column headerText="Name" style="text-align: center">
-                                    <p:inplace id="basic" editor="true">  
-                                        <p:inputText value="#{dispConn.name}" validator="#{currentCockpitEditorBean.validateDisplayConnectorName}" />
-                                    </p:inplace>  
-                                </p:column>
-                                <p:column style="text-align: center; width: 50px" >
-                                    <p:commandButton icon="ui-icon-trash" disabled="true"/>
-                                </p:column>
-                            </p:dataTable>
-                        </p:tab>
-                        <p:ajax event="tabChange" listener="#{currentCockpitEditorBean.onChange}" />
-                    </p:accordionPanel>
+                        </p:column>
+                        <p:column headerText="Column" style="text-align: center">
+                        </p:column>
+                        <p:column style="text-align: center; width: 50px" >
+                            <p:commandButton icon="ui-icon-trash" disabled="true"/>
+                        </p:column>
+                    </p:dataTable>
                 </h:form>
             </p:layoutUnit>
         </p:layout>
diff --git a/Kieker.WebGUI/src/main/webapp/dialogs/viewDialogs.xhtml b/Kieker.WebGUI/src/main/webapp/dialogs/viewDialogs.xhtml
index ac6c1095..7cf24bef 100644
--- a/Kieker.WebGUI/src/main/webapp/dialogs/viewDialogs.xhtml
+++ b/Kieker.WebGUI/src/main/webapp/dialogs/viewDialogs.xhtml
@@ -20,7 +20,7 @@
 
             <hr/>
             <div style="text-align: right">
-                <p:commandButton value="Ok" action="#{currentCockpitEditorBean.addView(stringBean.string)}" update=":messages :viewsForm" oncomplete="newViewDialog.hide()" />
+                <p:commandButton value="Ok" action="#{currentCockpitEditorBean.addView(stringBean.string)}" update=":messages :availableViewsForm" oncomplete="newViewDialog.hide()" />
                 <p:spacer width="10px" height="10" />
                 <p:commandButton value="Cancel" onclick="newViewDialog.hide()" />
             </div>
-- 
GitLab