From c425aea5b318f0c291804cf3973b3059f923e9b5 Mon Sep 17 00:00:00 2001 From: Bruno Sinou Date: Tue, 6 Sep 2011 10:12:29 +0000 Subject: [PATCH] Jcr Explorer refactoring and packaging git-svn-id: https://svn.argeo.org/commons/trunk@4725 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../org.argeo.jcr.ui.explorer/.classpath | 1 + .../META-INF/MANIFEST.MF | 59 +++- .../META-INF/spring/commands.xml | 9 +- .../META-INF/spring/editors.xml | 7 +- .../META-INF/spring/views.xml | 2 +- .../build.properties | 3 +- .../icons/repositories.gif | Bin 0 -> 588 bytes .../org.argeo.jcr.ui.explorer/plugin.xml | 22 +- .../plugins/org.argeo.jcr.ui.explorer/pom.xml | 9 +- .../jcr/ui/explorer/JcrExplorerConstants.java | 11 + .../ui/explorer/JcrExplorerPerspective.java | 19 ++ .../jcr/ui/explorer/JcrExplorerPlugin.java | 92 ++++++ .../jcr/ui/explorer/JcrExplorerView.java | 7 + .../explorer/browser/HomeContentProvider.java | 14 + .../ui/explorer/browser/ItemComparator.java | 20 ++ .../explorer/browser/NodeContentProvider.java | 132 ++++++++ .../explorer/browser/NodeLabelProvider.java | 52 +++ .../browser/PropertiesContentProvider.java | 40 +++ .../ui/explorer/browser/RepositoryNode.java | 49 +++ .../ui/explorer/browser/WorkspaceNode.java | 107 +++++++ .../jcr/ui/explorer/commands/EditNode.java | 54 ++++ .../explorer/commands/ImportFileSystem.java | 54 ++++ .../commands/OpenGenericJcrQueryEditor.java | 30 ++ .../commands/OpenGenericNodeEditor.java | 31 ++ .../ui/explorer/dialogs/ChooseNameDialog.java | 52 +++ .../ui/explorer/editors/EmptyNodePage.java | 36 +++ .../editors/GenericJcrQueryEditor.java | 44 +++ .../explorer/editors/GenericNodeEditor.java | 91 ++++++ .../editors/GenericNodeEditorInput.java | 98 ++++++ .../ui/explorer/editors/GenericNodePage.java | 199 ++++++++++++ .../utils/GenericNodeDoubleClickListener.java | 111 +++++++ .../ui/explorer/utils/JcrFileProvider.java | 150 +++++++++ .../jcr/ui/explorer/utils/JcrUiUtils.java | 5 + .../ui/explorer/views/GenericJcrBrowser.java | 300 ++++++++++++++++++ .../wizards/ImportFileSystemWizard.java | 220 +++++++++++++ .../argeo/jcr/ui/explorer/messages.properties | 8 + 36 files changed, 2124 insertions(+), 14 deletions(-) create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/icons/repositories.gif create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerConstants.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPerspective.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPlugin.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerView.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/HomeContentProvider.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/ItemComparator.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeContentProvider.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeLabelProvider.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/PropertiesContentProvider.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/RepositoryNode.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/WorkspaceNode.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/EditNode.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/ImportFileSystem.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericJcrQueryEditor.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericNodeEditor.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/dialogs/ChooseNameDialog.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/EmptyNodePage.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericJcrQueryEditor.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditor.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditorInput.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodePage.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/GenericNodeDoubleClickListener.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrFileProvider.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrUiUtils.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/views/GenericJcrBrowser.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ImportFileSystemWizard.java create mode 100644 server/plugins/org.argeo.jcr.ui.explorer/src/main/resources/org/argeo/jcr/ui/explorer/messages.properties diff --git a/server/plugins/org.argeo.jcr.ui.explorer/.classpath b/server/plugins/org.argeo.jcr.ui.explorer/.classpath index 92f19d2ff..d3d5c8095 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/.classpath +++ b/server/plugins/org.argeo.jcr.ui.explorer/.classpath @@ -3,5 +3,6 @@ + diff --git a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/MANIFEST.MF b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/MANIFEST.MF index 13fdc29a9..896f7fa36 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/MANIFEST.MF +++ b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/MANIFEST.MF @@ -3,17 +3,64 @@ Bundle-ManifestVersion: 2 Bundle-Name: JCR Explorer Bundle-SymbolicName: org.argeo.jcr.ui.explorer;singleton:=true Bundle-Version: 0.3.4.SNAPSHOT -Bundle-Activator: org.argeo.eclipse.ui.jcr.explorer.JcrExplorerPlugin +Bundle-Activator: org.argeo.jcr.ui.explorer.JcrExplorerPlugin Bundle-Vendor: Argeo Require-Bundle: org.eclipse.ui;resolution:=optional, - org.eclipse.core.runtime, - org.eclipse.rap.ui;resolution:=optional + org.eclipse.core.runtime;resolution:=optional, + org.eclipse.rap.ui;resolution:=optional, + org.eclipse.rap.ui.workbench;resolution:=optional Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ActivationPolicy: lazy -Import-Package: javax.jcr;version="2.0.0", +Import-Package: javax.jcr, + javax.jcr.nodetype, + javax.jcr.observation, + org.apache.commons.io, + org.apache.commons.logging, + org.argeo, org.argeo.eclipse.spring, + org.argeo.eclipse.ui, + org.argeo.eclipse.ui.jcr, org.argeo.eclipse.ui.jcr.commands, org.argeo.eclipse.ui.jcr.editors, + org.argeo.eclipse.ui.jcr.utils, org.argeo.eclipse.ui.jcr.views, - org.argeo.jcr -Export-Package: org.argeo.eclipse.ui.jcr.explorer + org.argeo.eclipse.ui.specific, + org.argeo.eclipse.ui.utils, + org.argeo.jcr, + org.argeo.jcr.spring, + org.argeo.util, + org.eclipse.ui.forms, + org.eclipse.ui.forms.editor, + org.eclipse.ui.forms.events, + org.eclipse.ui.forms.widgets, + org.springframework.context +Export-Package: org.argeo.jcr.ui.explorer, + org.argeo.jcr.ui.explorer.browser; + uses:="javax.jcr, + org.argeo.eclipse.ui.jcr, + org.argeo.jcr, + org.argeo, + org.eclipse.jface.viewers, + org.eclipse.jface.resource, + org.eclipse.swt.graphics, + javax.jcr.nodetype, + org.argeo.eclipse.ui, + javax.jcr.observation", + org.argeo.jcr.ui.explorer.dialogs; + uses:="org.eclipse.swt.graphics, + org.eclipse.swt.widgets, + org.eclipse.swt.layout, + org.eclipse.jface.dialogs", + org.argeo.jcr.ui.explorer.wizards; + uses:="javax.jcr, + org.argeo, + org.eclipse.jface.operation, + org.eclipse.core.runtime, + org.eclipse.jface.wizard, + org.apache.commons.logging, + org.argeo.eclipse.ui.dialogs, + org.argeo.eclipse.ui.specific, + org.apache.commons.io, + org.eclipse.swt.widgets, + org.eclipse.jface.dialogs" + diff --git a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml index 0a9a4bb61..16f4adfa6 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml +++ b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/commands.xml @@ -5,12 +5,17 @@ http://www.springframework.org/schema/beans/spring-beans.xsd"> + + + @@ -20,6 +25,6 @@ - diff --git a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/editors.xml b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/editors.xml index 6e88ffb3f..0dc6c91a4 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/editors.xml +++ b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/editors.xml @@ -5,7 +5,10 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd"> - + + + diff --git a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/views.xml b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/views.xml index eed1f4844..975dc5404 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/views.xml +++ b/server/plugins/org.argeo.jcr.ui.explorer/META-INF/spring/views.xml @@ -5,7 +5,7 @@ http://www.springframework.org/schema/beans/spring-beans.xsd"> - diff --git a/server/plugins/org.argeo.jcr.ui.explorer/build.properties b/server/plugins/org.argeo.jcr.ui.explorer/build.properties index 7b6db787b..1f316c6fa 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/build.properties +++ b/server/plugins/org.argeo.jcr.ui.explorer/build.properties @@ -1,4 +1,5 @@ -source.. = src/main/java/ +source.. = src/main/java/,\ + src/main/resources output.. = target/classes/ bin.includes = META-INF/,\ .,\ diff --git a/server/plugins/org.argeo.jcr.ui.explorer/icons/repositories.gif b/server/plugins/org.argeo.jcr.ui.explorer/icons/repositories.gif new file mode 100644 index 0000000000000000000000000000000000000000..c13bea1ca4123cee4276db0a468963bf2ca6fa94 GIT binary patch literal 588 zcmZ?wbhEHb6krfwc*el+`SaKR|EpfTI{fnG=|4Y~{Qb56-=9lAUseA8u<*~f|e{QkZp@nt_=Q$jntgwHs z^S;@xzusLuG*M|^zw(nU&hPg}?w;YXW19D?Gd<7G&w6-v-i{6PkF@g~YT?<}t+u06 z^TSrV2kSkyPxgB@op)a&-~LAaZ!2WCwQAp5>9=J<;LnHK9`p7<5360>udf`^tuZ06y_nalWR`t`=c7 zPX6|;E(v9M9b;E*RT0U#k|Iq~$_@<7Tq|8P%c2}fQEvenVYws`UykL0Nn!y3Z^oQ+>ZL!wE|2ISloN?$o#rdvz?#i z>xYh3*R7lQ)SSLPa$ff_z~xE2fYOO64PGTj6eh|0im9zS@FdzoO;km5)rTdIC-+D> KxACwrSOWlhFVLI- literal 0 HcmV?d00001 diff --git a/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml b/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml index ebec87a12..933aad083 100644 --- a/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml +++ b/server/plugins/org.argeo.jcr.ui.explorer/plugin.xml @@ -1,15 +1,17 @@ + + + + + + + + + + + 4.0.0 org.argeo.commons.server @@ -22,5 +23,11 @@ 0.3.4-SNAPSHOT provided + + org.argeo.commons.eclipse + org.argeo.eclipse.ui.rcp + 0.3.4-SNAPSHOT + provided + diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerConstants.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerConstants.java new file mode 100644 index 000000000..a410fcaec --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerConstants.java @@ -0,0 +1,11 @@ +package org.argeo.jcr.ui.explorer; + +/** Constants used across the application. */ +public interface JcrExplorerConstants { + + /* + * MISCEALLENEOUS + */ + public final static String DATE_TIME_FORMAT = "dd/MM/yyyy, HH:mm"; + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPerspective.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPerspective.java new file mode 100644 index 000000000..487784d4e --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPerspective.java @@ -0,0 +1,19 @@ +package org.argeo.jcr.ui.explorer; + +import org.eclipse.ui.IFolderLayout; +import org.eclipse.ui.IPageLayout; +import org.eclipse.ui.IPerspectiveFactory; + +public class JcrExplorerPerspective implements IPerspectiveFactory { + public static String BROWSER_VIEW_ID = JcrExplorerPlugin.ID + + ".browserView"; + + public void createInitialLayout(IPageLayout layout) { + layout.setEditorAreaVisible(true); + + IFolderLayout upperLeft = layout.createFolder(JcrExplorerPlugin.ID + + ".upperLeft", IPageLayout.LEFT, 0.4f, layout.getEditorArea()); + upperLeft.addView(BROWSER_VIEW_ID); + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPlugin.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPlugin.java new file mode 100644 index 000000000..c8aaab115 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerPlugin.java @@ -0,0 +1,92 @@ +package org.argeo.jcr.ui.explorer; + +import java.util.ResourceBundle; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class JcrExplorerPlugin extends AbstractUIPlugin { + private final static Log log = LogFactory.getLog(JcrExplorerPlugin.class); + private ResourceBundle messages; + + // The plug-in ID + public static final String ID = "org.argeo.jcr.ui.explorer"; //$NON-NLS-1$ + + // The shared instance + private static JcrExplorerPlugin plugin; + + /** + * The constructor + */ + public JcrExplorerPlugin() { + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext + * ) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + messages = ResourceBundle + .getBundle("org.argeo.jcr.ui.explorer.messages"); + + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext + * ) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static JcrExplorerPlugin getDefault() { + return plugin; + } + + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(ID, path); + } + + /** Returns the internationalized label for the given key */ + public static String getMessage(String key) { + try { + return getDefault().messages.getString(key); + } catch (NullPointerException npe) { + log.warn(key + " not found."); + return key; + } + } + + /** + * Gives access to the internationalization message bundle. Returns null in + * case the ClientUiPlugin is not started (for JUnit tests, by instance) + */ + public static ResourceBundle getMessagesBundle() { + if (getDefault() != null) + // To avoid NPE + return getDefault().messages; + else + return null; + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerView.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerView.java new file mode 100644 index 000000000..ac9c895bf --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/JcrExplorerView.java @@ -0,0 +1,7 @@ +package org.argeo.jcr.ui.explorer; + +import org.argeo.jcr.ui.explorer.views.GenericJcrBrowser; + +public class JcrExplorerView extends GenericJcrBrowser { + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/HomeContentProvider.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/HomeContentProvider.java new file mode 100644 index 000000000..123fdaa49 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/HomeContentProvider.java @@ -0,0 +1,14 @@ +package org.argeo.jcr.ui.explorer.browser; + +import javax.jcr.Session; + +import org.argeo.eclipse.ui.jcr.SimpleNodeContentProvider; +import org.argeo.jcr.JcrUtils; + +public class HomeContentProvider extends SimpleNodeContentProvider { + + public HomeContentProvider(Session session) { + super(session, new String[] { JcrUtils.getUserHomePath(session) }); + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/ItemComparator.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/ItemComparator.java new file mode 100644 index 000000000..156aa7b43 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/ItemComparator.java @@ -0,0 +1,20 @@ +package org.argeo.jcr.ui.explorer.browser; + +import java.util.Comparator; + +import javax.jcr.Item; +import javax.jcr.RepositoryException; + +import org.argeo.ArgeoException; + +public class ItemComparator implements Comparator { + public int compare(Item o1, Item o2) { + try { + // TODO: put folder before files + return o1.getName().compareTo(o2.getName()); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot compare " + o1 + " and " + o2, e); + } + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeContentProvider.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeContentProvider.java new file mode 100644 index 000000000..b2bd4b753 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeContentProvider.java @@ -0,0 +1,132 @@ +package org.argeo.jcr.ui.explorer.browser; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.ArgeoException; +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.RepositoryRegister; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +public class NodeContentProvider implements ITreeContentProvider { + private ItemComparator itemComparator = new ItemComparator(); + + private RepositoryRegister repositoryRegister; + private Session userSession; + + public NodeContentProvider(Session userSession, + RepositoryRegister repositoryRegister) { + this.userSession = userSession; + this.repositoryRegister = repositoryRegister; + } + + /** + * Sends back the first level of the Tree. Independent from inputElement + * that can be null + */ + public Object[] getElements(Object inputElement) { + List objs = new ArrayList(); + if (userSession != null) { + Node userHome = JcrUtils.getUserHome(userSession); + if (userHome != null) + objs.add(userHome); + } + if (repositoryRegister != null) + objs.add(repositoryRegister); + return objs.toArray(); + } + + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof Node) { + return childrenNodes((Node) parentElement); + } else if (parentElement instanceof RepositoryNode) { + return ((RepositoryNode) parentElement).getChildren(); + } else if (parentElement instanceof WorkspaceNode) { + Session session = ((WorkspaceNode) parentElement).getSession(); + if (session == null) + return new Object[0]; + + try { + return childrenNodes(session.getRootNode()); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot retrieve root node of " + + session, e); + } + } else if (parentElement instanceof RepositoryRegister) { + RepositoryRegister repositoryRegister = (RepositoryRegister) parentElement; + List nodes = new ArrayList(); + Map repositories = repositoryRegister + .getRepositories(); + for (String name : repositories.keySet()) { + nodes.add(new RepositoryNode(name, repositories.get(name))); + } + return nodes.toArray(); + } else { + return new Object[0]; + } + } + + public Object getParent(Object element) { + try { + if (element instanceof Node) { + Node node = (Node) element; + if (!node.getPath().equals("/")) + return node.getParent(); + else + return null; + } + return null; + } catch (RepositoryException e) { + return null; + } + } + + public boolean hasChildren(Object element) { + try { + if (element instanceof Node) { + return ((Node) element).hasNodes(); + } else if (element instanceof RepositoryNode) { + return ((RepositoryNode) element).hasChildren(); + } else if (element instanceof WorkspaceNode) { + return ((WorkspaceNode) element).getSession() != null; + } else if (element instanceof RepositoryRegister) { + return ((RepositoryRegister) element).getRepositories().size() > 0; + } + return false; + } catch (RepositoryException e) { + throw new ArgeoException("Cannot check children of " + element, e); + } + } + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + protected Object[] childrenNodes(Node parentNode) { + try { + List children = new ArrayList(); + NodeIterator nit = parentNode.getNodes(); + while (nit.hasNext()) { + Node node = nit.nextNode(); + children.add(node); + } + Node[] arr = children.toArray(new Node[children.size()]); + Arrays.sort(arr, itemComparator); + return arr; + } catch (RepositoryException e) { + throw new ArgeoException("Cannot list children of " + parentNode, e); + } + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeLabelProvider.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeLabelProvider.java new file mode 100644 index 000000000..6b9af8f75 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/NodeLabelProvider.java @@ -0,0 +1,52 @@ +package org.argeo.jcr.ui.explorer.browser; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.argeo.eclipse.ui.jcr.DefaultNodeLabelProvider; +import org.argeo.eclipse.ui.jcr.JcrUiPlugin; +import org.argeo.jcr.RepositoryRegister; +import org.eclipse.swt.graphics.Image; + +public class NodeLabelProvider extends DefaultNodeLabelProvider { + // Images + public final static Image REPOSITORIES = JcrUiPlugin.getImageDescriptor( + "icons/repositories.gif").createImage(); + + public String getText(Object element) { + if (element instanceof RepositoryRegister) { + return "Repositories"; + } + return super.getText(element); + } + + protected String getText(Node node) throws RepositoryException { + String label = node.getName(); + StringBuffer mixins = new StringBuffer(""); + for (NodeType type : node.getMixinNodeTypes()) + mixins.append(' ').append(type.getName()); + + return label + " [" + node.getPrimaryNodeType().getName() + mixins + + "]"; + } + + @Override + public Image getImage(Object element) { + if (element instanceof RepositoryNode) { + if (((RepositoryNode) element).getDefaultSession() == null) + return RepositoryNode.REPOSITORY_DISCONNECTED; + else + return RepositoryNode.REPOSITORY_CONNECTED; + } else if (element instanceof WorkspaceNode) { + if (((WorkspaceNode) element).getSession() == null) + return WorkspaceNode.WORKSPACE_DISCONNECTED; + else + return WorkspaceNode.WORKSPACE_CONNECTED; + } else if (element instanceof RepositoryRegister) { + return REPOSITORIES; + } + return super.getImage(element); + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/PropertiesContentProvider.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/PropertiesContentProvider.java new file mode 100644 index 000000000..04cd699a7 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/PropertiesContentProvider.java @@ -0,0 +1,40 @@ +package org.argeo.jcr.ui.explorer.browser; + +import java.util.Set; +import java.util.TreeSet; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.RepositoryException; + +import org.argeo.ArgeoException; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +public class PropertiesContentProvider implements IStructuredContentProvider { + private ItemComparator itemComparator = new ItemComparator(); + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + } + + public Object[] getElements(Object inputElement) { + try { + if (inputElement instanceof Node) { + Set props = new TreeSet(itemComparator); + PropertyIterator pit = ((Node) inputElement).getProperties(); + while (pit.hasNext()) + props.add(pit.nextProperty()); + return props.toArray(); + } + return new Object[] {}; + } catch (RepositoryException e) { + throw new ArgeoException("Cannot get element for " + inputElement, + e); + } + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/RepositoryNode.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/RepositoryNode.java new file mode 100644 index 000000000..a09661e37 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/RepositoryNode.java @@ -0,0 +1,49 @@ +package org.argeo.jcr.ui.explorer.browser; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.TreeParent; +import org.argeo.eclipse.ui.jcr.JcrUiPlugin; +import org.eclipse.swt.graphics.Image; + +public class RepositoryNode extends TreeParent { + private final String name; + private final Repository repository; + private Session defaultSession = null; + public final static Image REPOSITORY_DISCONNECTED = JcrUiPlugin + .getImageDescriptor("icons/repository_disconnected.gif") + .createImage(); + public final static Image REPOSITORY_CONNECTED = JcrUiPlugin + .getImageDescriptor("icons/repository_connected.gif").createImage(); + + public RepositoryNode(String name, Repository repository) { + super(name); + this.name = name; + this.repository = repository; + } + + public void login() { + try { + defaultSession = repository.login(); + String[] wkpNames = defaultSession.getWorkspace() + .getAccessibleWorkspaceNames(); + for (String wkpName : wkpNames) { + if (wkpName.equals(defaultSession.getWorkspace().getName())) + addChild(new WorkspaceNode(repository, wkpName, + defaultSession)); + else + addChild(new WorkspaceNode(repository, wkpName)); + } + } catch (RepositoryException e) { + throw new ArgeoException("Cannot connect to repository " + name, e); + } + } + + public Session getDefaultSession() { + return defaultSession; + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/WorkspaceNode.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/WorkspaceNode.java new file mode 100644 index 000000000..6c8b7dbfe --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/browser/WorkspaceNode.java @@ -0,0 +1,107 @@ +package org.argeo.jcr.ui.explorer.browser; + +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.observation.EventIterator; +import javax.jcr.observation.EventListener; + +import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.TreeParent; +import org.argeo.eclipse.ui.jcr.JcrUiPlugin; +import org.eclipse.swt.graphics.Image; + +public class WorkspaceNode extends TreeParent implements EventListener { + private final String name; + private final Repository repository; + private Session session = null; + public final static Image WORKSPACE_DISCONNECTED = JcrUiPlugin + .getImageDescriptor("icons/workspace_disconnected.png") + .createImage(); + public final static Image WORKSPACE_CONNECTED = JcrUiPlugin + .getImageDescriptor("icons/workspace_connected.png").createImage(); + + public WorkspaceNode(Repository repository, String name) { + this(repository, name, null); + } + + public WorkspaceNode(Repository repository, String name, Session session) { + super(name); + this.name = name; + this.repository = repository; + this.session = session; + if (session != null) + processNewSession(session); + } + + public Session getSession() { + return session; + } + + public void login() { + try { + logout(); + session = repository.login(name); + processNewSession(session); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot connect to repository " + name, e); + } + } + + public void logout() { + try { + if (session != null && session.isLive()) { + session.getWorkspace().getObservationManager() + .removeEventListener(this); + session.logout(); + } + } catch (RepositoryException e) { + throw new ArgeoException("Cannot connect to repository " + name, e); + } + } + + public void onEvent(final EventIterator events) { + // if (session == null) + // return; + // Display.getDefault().syncExec(new Runnable() { + // public void run() { + // while (events.hasNext()) { + // Event event = events.nextEvent(); + // try { + // String path = event.getPath(); + // String parentPath = path.substring(0, + // path.lastIndexOf('/')); + // final Object parent; + // if (parentPath.equals("/") || parentPath.equals("")) + // parent = this; + // else if (session.itemExists(parentPath)){ + // parent = session.getItem(parentPath); + // ((Item)parent).refresh(false); + // } + // else + // parent = null; + // if (parent != null) { + // nodesViewer.refresh(parent); + // } + // + // } catch (RepositoryException e) { + // log.warn("Error processing event " + event, e); + // } + // } + // } + // }); + } + + protected void processNewSession(Session session) { + // try { + // ObservationManager observationManager = session.getWorkspace() + // .getObservationManager(); + // observationManager.addEventListener(this, Event.NODE_ADDED + // | Event.NODE_REMOVED, "/", true, null, null, false); + // } catch (RepositoryException e) { + // throw new ArgeoException("Cannot process new session " + // + session, e); + // } + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/EditNode.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/EditNode.java new file mode 100644 index 000000000..16bb1b36d --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/EditNode.java @@ -0,0 +1,54 @@ +package org.argeo.jcr.ui.explorer.commands; + +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.Property; +import javax.jcr.nodetype.NodeType; + +import org.argeo.eclipse.ui.dialogs.Error; +import org.argeo.eclipse.ui.jcr.editors.NodeEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.handlers.HandlerUtil; + +/** Generic command to open a path in an editor. */ +public class EditNode extends AbstractHandler { + public final static String EDITOR_PARAM = "editor"; + + private String defaultEditorId; + + private Map nodeTypeToEditor = new HashMap(); + + public Object execute(ExecutionEvent event) throws ExecutionException { + String path = event.getParameter(Property.JCR_PATH); + + String type = event.getParameter(NodeType.NT_NODE_TYPE); + if (type == null) + type = NodeType.NT_UNSTRUCTURED; + + String editorId = event.getParameter(NodeType.NT_NODE_TYPE); + if (editorId == null) + editorId = nodeTypeToEditor.containsKey(type) ? nodeTypeToEditor + .get(type) : defaultEditorId; + + NodeEditorInput nei = new NodeEditorInput(path); + + try { + HandlerUtil.getActiveWorkbenchWindow(event).getActivePage() + .openEditor(nei, editorId); + } catch (PartInitException e) { + Error.show("Cannot open " + editorId + " with " + path + + " of type " + type, e); + } + // TODO Auto-generated method stub + return null; + } + + public void setDefaultEditorId(String defaultEditorId) { + this.defaultEditorId = defaultEditorId; + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/ImportFileSystem.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/ImportFileSystem.java new file mode 100644 index 000000000..12880bad0 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/ImportFileSystem.java @@ -0,0 +1,54 @@ +package org.argeo.jcr.ui.explorer.commands; + +import javax.jcr.Node; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.eclipse.ui.dialogs.Error; +import org.argeo.eclipse.ui.jcr.views.AbstractJcrBrowser; +import org.argeo.jcr.ui.explorer.wizards.ImportFileSystemWizard; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.ui.handlers.HandlerUtil; + +public class ImportFileSystem extends AbstractHandler { + private static Log log = LogFactory.getLog(ImportFileSystem.class); + + public Object execute(ExecutionEvent event) throws ExecutionException { + ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event) + .getActivePage().getSelection(); + AbstractJcrBrowser view = (AbstractJcrBrowser) HandlerUtil + .getActiveWorkbenchWindow(event).getActivePage() + .findView(HandlerUtil.getActivePartId(event)); + if (selection != null && !selection.isEmpty() + && selection instanceof IStructuredSelection) { + Object obj = ((IStructuredSelection) selection).getFirstElement(); + try { + if (obj instanceof Node) { + Node folder = (Node) obj; + // if (!folder.getPrimaryNodeType().getName() + // .equals(NodeType.NT_FOLDER)) { + // Error.show("Can only import to a folder node"); + // return null; + // } + ImportFileSystemWizard wizard = new ImportFileSystemWizard( + folder); + WizardDialog dialog = new WizardDialog( + HandlerUtil.getActiveShell(event), wizard); + dialog.open(); + view.refresh(folder); + } else { + Error.show("Can only import to a node"); + } + } catch (Exception e) { + Error.show("Cannot import files to " + obj, e); + } + } + return null; + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericJcrQueryEditor.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericJcrQueryEditor.java new file mode 100644 index 000000000..177dfd28c --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericJcrQueryEditor.java @@ -0,0 +1,30 @@ +package org.argeo.jcr.ui.explorer.commands; + +import org.argeo.eclipse.ui.jcr.editors.JcrQueryEditorInput; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.handlers.HandlerUtil; + +/** Open a JCR query editor. */ +public class OpenGenericJcrQueryEditor extends AbstractHandler { + private String editorId; + + public Object execute(ExecutionEvent event) throws ExecutionException { + try { + JcrQueryEditorInput editorInput = new JcrQueryEditorInput("", null); + IWorkbenchPage activePage = HandlerUtil.getActiveWorkbenchWindow( + event).getActivePage(); + activePage.openEditor(editorInput, editorId); + } catch (Exception e) { + throw new ExecutionException("Cannot open editor", e); + } + return null; + } + + public void setEditorId(String editorId) { + this.editorId = editorId; + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericNodeEditor.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericNodeEditor.java new file mode 100644 index 000000000..63d53fa3f --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/commands/OpenGenericNodeEditor.java @@ -0,0 +1,31 @@ +package org.argeo.jcr.ui.explorer.commands; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.jcr.editors.NodeEditorInput; +import org.argeo.jcr.ui.explorer.editors.GenericNodeEditor; +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.handlers.HandlerUtil; + +public class OpenGenericNodeEditor extends AbstractHandler { + private final static Log log = LogFactory + .getLog(OpenGenericNodeEditor.class); + public final static String ID = "org.argeo.jcr.ui.explorer.openGenericNodeEditor"; + public final static String PARAM_PATH = "org.argeo.jcr.ui.explorer.nodePath"; + + public Object execute(ExecutionEvent event) throws ExecutionException { + String path = event.getParameter(PARAM_PATH); + try { + NodeEditorInput nei = new NodeEditorInput(path); + HandlerUtil.getActiveWorkbenchWindow(event).getActivePage() + .openEditor(nei, GenericNodeEditor.ID); + } catch (Exception e) { + throw new ArgeoException("Cannot open editor", e); + } + return null; + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/dialogs/ChooseNameDialog.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/dialogs/ChooseNameDialog.java new file mode 100644 index 000000000..01957f308 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/dialogs/ChooseNameDialog.java @@ -0,0 +1,52 @@ +package org.argeo.jcr.ui.explorer.dialogs; + +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.dialogs.TitleAreaDialog; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** Dialog to change the current user password */ +public class ChooseNameDialog extends TitleAreaDialog { + private Text nameT; + + public ChooseNameDialog(Shell parentShell) { + super(parentShell); + setTitle("Choose name"); + } + + protected Point getInitialSize() { + return new Point(300, 250); + } + + protected Control createDialogArea(Composite parent) { + Composite dialogarea = (Composite) super.createDialogArea(parent); + dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + Composite composite = new Composite(dialogarea, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + nameT = createLT(composite, "Name"); + + setMessage("Choose name", IMessageProvider.INFORMATION); + parent.pack(); + return composite; + } + + /** Creates label and text. */ + protected Text createLT(Composite parent, String label) { + new Label(parent, SWT.NONE).setText(label); + Text text = new Text(parent, SWT.SINGLE | SWT.LEAD | SWT.BORDER); + text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + return text; + } + + public String getName() { + return nameT.getText(); + } +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/EmptyNodePage.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/EmptyNodePage.java new file mode 100644 index 000000000..b9f20b9cf --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/EmptyNodePage.java @@ -0,0 +1,36 @@ +package org.argeo.jcr.ui.explorer.editors; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.forms.IManagedForm; +import org.eclipse.ui.forms.editor.FormEditor; +import org.eclipse.ui.forms.editor.FormPage; +import org.eclipse.ui.forms.widgets.ScrolledForm; + +/** + * This page is only used at editor's creation time when current node has not + * yet been set + */ +public class EmptyNodePage extends FormPage { + private final static Log log = LogFactory.getLog(EmptyNodePage.class); + + public EmptyNodePage(FormEditor editor, String title) { + super(editor, "Empty Page", title); + } + + protected void createFormContent(IManagedForm managedForm) { + try { + ScrolledForm form = managedForm.getForm(); + GridLayout twt = new GridLayout(1, false); + twt.marginWidth = twt.marginHeight = 0; + form.getBody().setLayout(twt); + Label lbl = new Label(form.getBody(), SWT.NONE); + lbl.setText("Empty page"); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericJcrQueryEditor.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericJcrQueryEditor.java new file mode 100644 index 000000000..7dcb7c0b6 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericJcrQueryEditor.java @@ -0,0 +1,44 @@ +package org.argeo.jcr.ui.explorer.editors; + +import org.argeo.eclipse.ui.jcr.editors.AbstractJcrQueryEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Text; + +/** Executes any JCR query. */ +public class GenericJcrQueryEditor extends AbstractJcrQueryEditor { + public final static String ID = "org.argeo.jcr.ui.explorer.genericJcrQueryEditor"; + + private Text queryField; + + @Override + public void createQueryForm(Composite parent) { + parent.setLayout(new GridLayout(1, false)); + + queryField = new Text(parent, SWT.BORDER | SWT.MULTI | SWT.WRAP); + queryField.setText(initialQuery); + queryField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + Button execute = new Button(parent, SWT.PUSH); + execute.setText("Execute"); + + Listener executeListener = new Listener() { + public void handleEvent(Event event) { + executeQuery(queryField.getText()); + } + }; + + execute.addListener(SWT.Selection, executeListener); + // queryField.addListener(SWT.DefaultSelection, executeListener); + } + + @Override + public void setFocus() { + queryField.setFocus(); + } +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditor.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditor.java new file mode 100644 index 000000000..4510ea3fc --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditor.java @@ -0,0 +1,91 @@ +package org.argeo.jcr.ui.explorer.editors; + +import javax.jcr.Node; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.ui.explorer.JcrExplorerPlugin; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.forms.editor.FormEditor; + +/** + * Parent Abstract GR multitab editor. Insure the presence of a GrBackend + */ +public class GenericNodeEditor extends FormEditor { + + private final static Log log = LogFactory.getLog(GenericNodeEditor.class); + public final static String ID = "org.argeo.jcr.ui.explorer.genericNodeEditor"; + + private Node currentNode; + + private GenericNodePage networkDetailsPage; + + public void init(IEditorSite site, IEditorInput input) + throws PartInitException { + super.init(site, input); + GenericNodeEditorInput nei = (GenericNodeEditorInput) getEditorInput(); + this.setPartName(JcrUtils.lastPathElement(nei.getPath())); + } + + @Override + protected void addPages() { + EmptyNodePage enp = new EmptyNodePage(this, "Empty node page"); + try { + addPage(enp); + } catch (PartInitException e) { + throw new ArgeoException("Not able to add an empty page ", e); + } + } + + private void addPagesAfterNodeSet() { + try { + networkDetailsPage = new GenericNodePage(this, + JcrExplorerPlugin.getMessage("genericNodePageTitle"), + currentNode); + addPage(networkDetailsPage); + this.setActivePage(networkDetailsPage.getIndex()); + } catch (PartInitException e) { + throw new ArgeoException("Not able to add page ", e); + } + } + + @Override + public void doSaveAs() { + // unused compulsory method + } + + @Override + public void doSave(IProgressMonitor monitor) { + try { + // Automatically commit all pages of the editor + commitPages(true); + firePropertyChange(PROP_DIRTY); + } catch (Exception e) { + throw new ArgeoException("Error while saving node", e); + } + + } + + @Override + public boolean isSaveAsAllowed() { + return true; + } + + Node getCurrentNode() { + return currentNode; + } + + public void setCurrentNode(Node currentNode) { + boolean nodeWasNull = this.currentNode == null; + this.currentNode = currentNode; + if (nodeWasNull) { + this.removePage(0); + addPagesAfterNodeSet(); + } + } +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditorInput.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditorInput.java new file mode 100644 index 000000000..9eceb622b --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodeEditorInput.java @@ -0,0 +1,98 @@ +package org.argeo.jcr.ui.explorer.editors; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IPersistableElement; + +/** + * An editor input based on a path to a node plus workspace name and repository + * alias. In a multirepository environment, path can be enriched with Repository + * Alias and workspace + */ + +public class GenericNodeEditorInput implements IEditorInput { + private final String path; + private final String repositoryAlias; + private final String workspaceName; + + /** + * In order to implement a generic explorer that supports remote and multi + * workspaces repositories, node path can be detailed by these strings. + * + * @param repositoryAlias + * : can be null + * @param workspaceName + * : can be null + * @param path + */ + public GenericNodeEditorInput(String repositoryAlias, String workspaceName, + String path) { + this.path = path; + this.repositoryAlias = repositoryAlias; + this.workspaceName = workspaceName; + } + + public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { + return null; + } + + public boolean exists() { + return true; + } + + public ImageDescriptor getImageDescriptor() { + return null; + } + + public String getName() { + return path; + } + + public String getRepositoryAlias() { + return repositoryAlias; + } + + public String getWorkspaceName() { + return workspaceName; + } + + public IPersistableElement getPersistable() { + return null; + } + + public String getToolTipText() { + return path; + } + + public String getPath() { + return path; + } + + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + + GenericNodeEditorInput other = (GenericNodeEditorInput) obj; + + if (!path.equals(other.getPath())) + return false; + + String own = other.getWorkspaceName(); + if ((workspaceName == null && own != null) + || (workspaceName != null && (own == null || !workspaceName + .equals(own)))) + return false; + + String ora = other.getRepositoryAlias(); + if ((repositoryAlias == null && ora != null) + || (repositoryAlias != null && (ora == null || !repositoryAlias + .equals(ora)))) + return false; + + return true; + } +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodePage.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodePage.java new file mode 100644 index 000000000..0be0f1949 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/editors/GenericNodePage.java @@ -0,0 +1,199 @@ +package org.argeo.jcr.ui.explorer.editors; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.ListIterator; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.ui.explorer.JcrExplorerConstants; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.forms.AbstractFormPart; +import org.eclipse.ui.forms.IManagedForm; +import org.eclipse.ui.forms.editor.FormEditor; +import org.eclipse.ui.forms.editor.FormPage; +import org.eclipse.ui.forms.widgets.FormToolkit; +import org.eclipse.ui.forms.widgets.ScrolledForm; + +public class GenericNodePage extends FormPage implements JcrExplorerConstants { + private final static Log log = LogFactory.getLog(GenericNodePage.class); + + // local constants + private final static String JCR_PROPERTY_NAME = "jcr:name"; + + // Utils + protected DateFormat timeFormatter = new SimpleDateFormat(DATE_TIME_FORMAT); + + // Main business Objects + private Node currentNode; + + // This page widgets + private FormToolkit tk; + private List modifyableProperties = new ArrayList(); + + public GenericNodePage(FormEditor editor, String title, Node currentNode) { + super(editor, "id", title); + this.currentNode = currentNode; + } + + protected void createFormContent(IManagedForm managedForm) { + try { + tk = managedForm.getToolkit(); + ScrolledForm form = managedForm.getForm(); + GridLayout twt = new GridLayout(3, false); + twt.marginWidth = twt.marginHeight = 0; + + form.getBody().setLayout(twt); + + createPropertiesPart(form.getBody()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void createPropertiesPart(Composite parent) { + try { + + PropertyIterator pi = currentNode.getProperties(); + + // Initializes form part + AbstractFormPart part = new AbstractFormPart() { + public void commit(boolean onSave) { + try { + if (onSave) { + ListIterator it = modifyableProperties + .listIterator(); + while (it.hasNext()) { + // we only support Text controls for the time + // being + Text curControl = (Text) it.next(); + String value = curControl.getText(); + currentNode.setProperty((String) curControl + .getData(JCR_PROPERTY_NAME), value); + } + + // We only commit when onSave = true, + // thus it is still possible to save after a tab + // change. + super.commit(onSave); + } + } catch (RepositoryException re) { + throw new ArgeoException( + "Unexpected error while saving properties", re); + } + } + }; + + while (pi.hasNext()) { + Property prop = pi.nextProperty(); + addPropertyLine(parent, part, prop); + } + + getManagedForm().addPart(part); + } catch (RepositoryException re) { + throw new ArgeoException( + "Error during creation of network details section", re); + } + + } + + private void addPropertyLine(Composite parent, AbstractFormPart part, + Property prop) { + try { + Label lbl = tk.createLabel(parent, prop.getName()); + lbl = tk.createLabel(parent, + "[" + JcrUtils.getPropertyDefinitionAsString(prop) + "]"); + + if (prop.getDefinition().isProtected()) { + lbl = tk.createLabel(parent, formatReadOnlyPropertyValue(prop)); + } else + addModifyableValueWidget(parent, part, prop); + } catch (RepositoryException re) { + throw new ArgeoException("Cannot get property " + prop, re); + } + } + + private String formatReadOnlyPropertyValue(Property prop) { + try { + String strValue; + + if (prop.getType() == PropertyType.BINARY) + strValue = ""; + else if (prop.isMultiple()) + strValue = Arrays.asList(prop.getValues()).toString(); + else if (prop.getType() == PropertyType.DATE) + strValue = timeFormatter.format(prop.getValue().getDate() + .getTime()); + else + strValue = prop.getValue().getString(); + + return strValue; + } catch (RepositoryException re) { + throw new ArgeoException( + "Unexpected error while formatting read only property value", + re); + } + } + + private Control addModifyableValueWidget(Composite parent, + AbstractFormPart part, Property prop) { + GridData gd; + try { + if (prop.getType() == PropertyType.STRING) { + Text txt = tk.createText(parent, prop.getString()); + gd = new GridData(GridData.FILL_HORIZONTAL); + txt.setLayoutData(gd); + txt.addModifyListener(new ModifiedFieldListener(part)); + txt.setData(JCR_PROPERTY_NAME, prop.getName()); + modifyableProperties.add(txt); + } else { + // unsupported property type for editing, we create a read only + // label. + return tk + .createLabel(parent, formatReadOnlyPropertyValue(prop)); + } + return null; + } catch (RepositoryException re) { + throw new ArgeoException( + "Unexpected error while formatting read only property value", + re); + } + + } + + // + // LISTENERS + // + + private class ModifiedFieldListener implements ModifyListener { + + private AbstractFormPart formPart; + + public ModifiedFieldListener(AbstractFormPart generalPart) { + this.formPart = generalPart; + } + + public void modifyText(ModifyEvent e) { + formPart.markDirty(); + } + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/GenericNodeDoubleClickListener.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/GenericNodeDoubleClickListener.java new file mode 100644 index 000000000..708c024e1 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/GenericNodeDoubleClickListener.java @@ -0,0 +1,111 @@ +package org.argeo.jcr.ui.explorer.utils; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.specific.FileHandler; +import org.argeo.jcr.ui.explorer.JcrExplorerPlugin; +import org.argeo.jcr.ui.explorer.browser.NodeContentProvider; +import org.argeo.jcr.ui.explorer.browser.RepositoryNode; +import org.argeo.jcr.ui.explorer.browser.WorkspaceNode; +import org.argeo.jcr.ui.explorer.editors.GenericNodeEditor; +import org.argeo.jcr.ui.explorer.editors.GenericNodeEditorInput; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.ui.PartInitException; + +/** + * + * Centralizes the management of double click on a NodeTreeViewer + * + */ +public class GenericNodeDoubleClickListener implements IDoubleClickListener { + + private final static Log log = LogFactory + .getLog(GenericNodeDoubleClickListener.class); + + private TreeViewer nodeViewer; + private JcrFileProvider jfp; + private FileHandler fileHandler; + + public GenericNodeDoubleClickListener(TreeViewer nodeViewer) { + this.nodeViewer = nodeViewer; + jfp = new JcrFileProvider(); + fileHandler = new FileHandler(jfp); + } + + public void doubleClick(DoubleClickEvent event) { + if (event.getSelection() == null || event.getSelection().isEmpty()) + return; + Object obj = ((IStructuredSelection) event.getSelection()) + .getFirstElement(); + if (obj instanceof RepositoryNode) { + RepositoryNode rpNode = (RepositoryNode) obj; + rpNode.login(); + nodeViewer.refresh(obj); + } else if (obj instanceof WorkspaceNode) { + ((WorkspaceNode) obj).login(); + nodeViewer.refresh(obj); + } else if (obj instanceof Node) { + Node node = (Node) obj; + try { + if (node.isNodeType(NodeType.NT_FILE)) { + // double click on a file node triggers its opening + String name = node.getName(); + String id = node.getIdentifier(); + + // For the file provider to be able to browse the + // various + // repository. + // TODO : enhanced that. + ITreeContentProvider itcp = (ITreeContentProvider) nodeViewer + .getContentProvider(); + jfp.setRootNodes((Object[]) itcp.getElements(null)); + fileHandler.openFile(name, id); + } + // File or not, we always open the corresponding node Editor. + String repositoryAlias = getRepositoryAlias(obj); + String workspaceName = node.getSession().getWorkspace() + .getName(); + String path = node.getPath(); + + if (log.isDebugEnabled()) { + log.debug("RepoAlias: " + repositoryAlias + " - WS Name: " + + workspaceName + " - path:" + path); + } + GenericNodeEditorInput gnei = new GenericNodeEditorInput( + repositoryAlias, workspaceName, path); + + GenericNodeEditor gne = (GenericNodeEditor) JcrExplorerPlugin + .getDefault().getWorkbench().getActiveWorkbenchWindow() + .getActivePage().openEditor(gnei, GenericNodeEditor.ID); + gne.setCurrentNode(node); + + } catch (RepositoryException re) { + throw new ArgeoException( + "Repository error while getting node info", re); + } catch (PartInitException pie) { + throw new ArgeoException( + "Unexepected exception while opening node editor", pie); + } + } + } + + // Enhance this method + private String getRepositoryAlias(Object element) { + NodeContentProvider ncp = (NodeContentProvider) nodeViewer + .getContentProvider(); + Object parent = element; + while (!(ncp.getParent(parent) instanceof RepositoryNode) + && parent != null) + parent = ncp.getParent(parent); + return parent == null ? null : ((RepositoryNode) parent).getName(); + } +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrFileProvider.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrFileProvider.java new file mode 100644 index 000000000..01fd634b5 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrFileProvider.java @@ -0,0 +1,150 @@ +package org.argeo.jcr.ui.explorer.utils; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.IOUtils; +import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.specific.FileProvider; +import org.argeo.jcr.RepositoryRegister; +import org.argeo.jcr.ui.explorer.browser.RepositoryNode; + +/** + * Implements a FileProvider for UI purposes. Note that it might not be very + * reliable as long as we have not fixed login & multi repository issues that + * will be addressed in the next version. + * + * NOTE: id used here is the real id of the JCR Node, not the JCR Path + * + * Relies on common approach for JCR file handling implementation. + * + */ + +public class JcrFileProvider implements FileProvider { + + private Object[] rootNodes; + + /** + * Must be set in order for the provider to be able to search the repository + * Provided object might be either JCR Nodes or UI RepositoryNode for the + * time being. + * + * @param repositoryNode + */ + public void setRootNodes(Object[] rootNodes) { + List tmpNodes = new ArrayList(); + for (int i = 0; i < rootNodes.length; i++) { + Object obj = rootNodes[i]; + if (obj instanceof Node) { + tmpNodes.add(obj); + } else if (obj instanceof RepositoryRegister) { + RepositoryRegister repositoryRegister = (RepositoryRegister) obj; + Map repositories = repositoryRegister + .getRepositories(); + for (String name : repositories.keySet()) { + tmpNodes.add(new RepositoryNode(name, repositories + .get(name))); + } + + } + } + this.rootNodes = tmpNodes.toArray(); + } + + public byte[] getByteArrayFileFromId(String fileId) { + InputStream fis = null; + byte[] ba = null; + Node child = getFileNodeFromId(fileId); + try { + fis = (InputStream) child.getProperty(Property.JCR_DATA) + .getBinary().getStream(); + ba = IOUtils.toByteArray(fis); + + } catch (Exception e) { + throw new ArgeoException("Stream error while opening file", e); + } finally { + IOUtils.closeQuietly(fis); + } + return ba; + } + + public InputStream getInputStreamFromFileId(String fileId) { + try { + InputStream fis = null; + + Node child = getFileNodeFromId(fileId); + fis = (InputStream) child.getProperty(Property.JCR_DATA) + .getBinary().getStream(); + return fis; + } catch (RepositoryException re) { + throw new ArgeoException("Cannot get stream from file node for Id " + + fileId, re); + } + } + + /** + * Throws an exception if the node is not found in the current repository (a + * bit like a FileNotFoundException) + * + * @param fileId + * @return Returns the child node of the nt:file node. It is the child node + * that have the jcr:data property where actual file is stored. + * never null + */ + private Node getFileNodeFromId(String fileId) { + try { + Node result = null; + + rootNodes: for (int j = 0; j < rootNodes.length; j++) { + // in case we have a classic JCR Node + if (rootNodes[j] instanceof Node) { + Node curNode = (Node) rootNodes[j]; + result = curNode.getSession().getNodeByIdentifier(fileId); + if (result != null) + break rootNodes; + } // Case of a repository Node + else if (rootNodes[j] instanceof RepositoryNode) { + Object[] nodes = ((RepositoryNode) rootNodes[j]) + .getChildren(); + for (int i = 0; i < nodes.length; i++) { + Node node = (Node) nodes[i]; + result = node.getSession().getNodeByIdentifier(fileId); + if (result != null) + break rootNodes; + } + } + } + + // Sanity checks + if (result == null) + throw new ArgeoException("File node not found for ID" + fileId); + + // Ensure that the node have the correct type. + if (!result.isNodeType(NodeType.NT_FILE)) + throw new ArgeoException( + "Cannot open file children Node that are not of '" + + NodeType.NT_RESOURCE + "' type."); + + // Get the usefull part of the Node + Node child = result.getNodes().nextNode(); + if (child == null || !child.isNodeType(NodeType.NT_RESOURCE)) + throw new ArgeoException( + "ERROR: IN the current implemented model, '" + + NodeType.NT_FILE + + "' file node must have one and only one child of the nt:ressource, where actual data is stored"); + return child; + + } catch (RepositoryException re) { + throw new ArgeoException("Erreur while getting file node of ID " + + fileId, re); + } + } +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrUiUtils.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrUiUtils.java new file mode 100644 index 000000000..72bd9ab11 --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/utils/JcrUiUtils.java @@ -0,0 +1,5 @@ +package org.argeo.jcr.ui.explorer.utils; + +public class JcrUiUtils { + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/views/GenericJcrBrowser.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/views/GenericJcrBrowser.java new file mode 100644 index 000000000..d009a85fb --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/views/GenericJcrBrowser.java @@ -0,0 +1,300 @@ +package org.argeo.jcr.ui.explorer.views; + +import java.util.Arrays; +import java.util.List; + +import javax.jcr.Property; +import javax.jcr.PropertyType; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.observation.Event; +import javax.jcr.observation.EventListener; +import javax.jcr.observation.ObservationManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.jcr.AsyncUiEventListener; +import org.argeo.eclipse.ui.jcr.utils.NodeViewerComparer; +import org.argeo.eclipse.ui.jcr.views.AbstractJcrBrowser; +import org.argeo.eclipse.ui.specific.FileHandler; +import org.argeo.jcr.ArgeoJcrConstants; +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.RepositoryRegister; +import org.argeo.jcr.ui.explorer.browser.NodeContentProvider; +import org.argeo.jcr.ui.explorer.browser.NodeLabelProvider; +import org.argeo.jcr.ui.explorer.browser.PropertiesContentProvider; +import org.argeo.jcr.ui.explorer.utils.GenericNodeDoubleClickListener; +import org.argeo.jcr.ui.explorer.utils.JcrFileProvider; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Menu; + +public class GenericJcrBrowser extends AbstractJcrBrowser { + private final static Log log = LogFactory.getLog(GenericJcrBrowser.class); + + /* DEPENDENCY INJECTION */ + private Session session; + private RepositoryRegister repositoryRegister; + + // This page widgets + private TreeViewer nodesViewer; + private NodeContentProvider nodeContentProvider; + private TableViewer propertiesViewer; + private EventListener resultsObserver; + + // Manage documents + private JcrFileProvider jcrFileProvider; + private FileHandler fileHandler; + + @Override + public void createPartControl(Composite parent) { + + // look for session + Session nodeSession = session; + if (nodeSession == null) { + Repository nodeRepository = JcrUtils.getRepositoryByAlias( + repositoryRegister, ArgeoJcrConstants.ALIAS_NODE); + if (nodeRepository != null) + try { + nodeSession = nodeRepository.login(); + // TODO : enhance that to enable multirepository listener. + session = nodeSession; + } catch (RepositoryException e1) { + throw new ArgeoException("Cannot login to node repository"); + } + } + + // Instantiate the generic object that fits for + // both RCP & RAP + // Note that in RAP, it registers a service handler that provide the + // access to the files. + jcrFileProvider = new JcrFileProvider(); + fileHandler = new FileHandler(jcrFileProvider); + + parent.setLayout(new FillLayout()); + SashForm sashForm = new SashForm(parent, SWT.VERTICAL); + sashForm.setSashWidth(4); + sashForm.setLayout(new FillLayout()); + + // Create the tree on top of the view + Composite top = new Composite(sashForm, SWT.NONE); + GridLayout gl = new GridLayout(1, false); + top.setLayout(gl); + + nodeContentProvider = new NodeContentProvider(nodeSession, + repositoryRegister); + + // nodes viewer + nodesViewer = createNodeViewer(top, nodeContentProvider); + + // context menu + MenuManager menuManager = new MenuManager(); + Menu menu = menuManager.createContextMenu(nodesViewer.getTree()); + nodesViewer.getTree().setMenu(menu); + getSite().registerContextMenu(menuManager, nodesViewer); + getSite().setSelectionProvider(nodesViewer); + nodesViewer.setInput(getViewSite()); + + // Create the property viewer on the bottom + Composite bottom = new Composite(sashForm, SWT.NONE); + bottom.setLayout(new GridLayout(1, false)); + propertiesViewer = new TableViewer(bottom); + propertiesViewer.getTable().setLayoutData( + new GridData(SWT.FILL, SWT.FILL, true, true)); + propertiesViewer.getTable().setHeaderVisible(true); + propertiesViewer.setContentProvider(new PropertiesContentProvider()); + TableViewerColumn col = new TableViewerColumn(propertiesViewer, + SWT.NONE); + col.getColumn().setText("Name"); + col.getColumn().setWidth(200); + col.setLabelProvider(new ColumnLabelProvider() { + public String getText(Object element) { + try { + return ((Property) element).getName(); + } catch (RepositoryException e) { + throw new ArgeoException( + "Unexpected exception in label provider", e); + } + } + }); + col = new TableViewerColumn(propertiesViewer, SWT.NONE); + col.getColumn().setText("Value"); + col.getColumn().setWidth(400); + col.setLabelProvider(new ColumnLabelProvider() { + public String getText(Object element) { + try { + Property property = (Property) element; + if (property.getType() == PropertyType.BINARY) + return ""; + else if (property.isMultiple()) + return Arrays.asList(property.getValues()).toString(); + else + return property.getValue().getString(); + } catch (RepositoryException e) { + throw new ArgeoException( + "Unexpected exception in label provider", e); + } + } + }); + col = new TableViewerColumn(propertiesViewer, SWT.NONE); + col.getColumn().setText("Type"); + col.getColumn().setWidth(200); + col.setLabelProvider(new ColumnLabelProvider() { + public String getText(Object element) { + try { + return PropertyType.nameFromValue(((Property) element) + .getType()); + } catch (RepositoryException e) { + throw new ArgeoException( + "Unexpected exception in label provider", e); + } + } + }); + propertiesViewer.setInput(getViewSite()); + + sashForm.setWeights(getWeights()); + nodesViewer.setComparer(new NodeViewerComparer()); + } + + /** + * To be overridden to adapt size of form and result frames. + */ + protected int[] getWeights() { + return new int[] { 70, 30 }; + } + + // @Override + // public void setFocus() { + // nodesViewer.getTree().setFocus(); + // } + // + // /* + // * NOTIFICATION + // */ + // public void refresh(Object obj) { + // nodesViewer.refresh(obj); + // } + // + // public void nodeAdded(Node parentNode, Node newNode) { + // nodesViewer.refresh(parentNode); + // nodesViewer.expandToLevel(newNode, 0); + // } + // + // public void nodeRemoved(Node parentNode) { + // + // IStructuredSelection newSel = new StructuredSelection(parentNode); + // nodesViewer.setSelection(newSel, true); + // // Force refresh + // IStructuredSelection tmpSel = (IStructuredSelection) nodesViewer + // .getSelection(); + // nodesViewer.refresh(tmpSel.getFirstElement()); + // } + + private JcrFileProvider getJcrFileProvider() { + return jcrFileProvider; + } + + private FileHandler getFileHandler() { + return fileHandler; + } + + protected TreeViewer createNodeViewer(Composite parent, + final ITreeContentProvider nodeContentProvider) { + + final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI); + + tmpNodeViewer.getTree().setLayoutData( + new GridData(SWT.FILL, SWT.FILL, true, true)); + + tmpNodeViewer.setContentProvider(nodeContentProvider); + tmpNodeViewer.setLabelProvider(new NodeLabelProvider()); + tmpNodeViewer + .addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + if (!event.getSelection().isEmpty()) { + IStructuredSelection sel = (IStructuredSelection) event + .getSelection(); + propertiesViewer.setInput(sel.getFirstElement()); + } else { + propertiesViewer.setInput(getViewSite()); + } + } + }); + + resultsObserver = new TreeObserver(tmpNodeViewer.getTree().getDisplay()); + try { + ObservationManager observationManager = session.getWorkspace() + .getObservationManager(); + // FIXME Will not be notified if empty result is deleted + observationManager.addEventListener(resultsObserver, + Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED, "/", true, + null, null, false); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot register listeners", e); + } + + tmpNodeViewer + .addDoubleClickListener(new GenericNodeDoubleClickListener( + tmpNodeViewer)); + return tmpNodeViewer; + } + + @Override + protected TreeViewer getNodeViewer() { + return nodesViewer; + } + + class TreeObserver extends AsyncUiEventListener { + + public TreeObserver(Display display) { + super(display); + } + + @Override + protected Boolean willProcessInUiThread(List events) + throws RepositoryException { + for (Event event : events) { + getLog().debug("Received event " + event); + String path = event.getPath(); + int index = path.lastIndexOf('/'); + String propertyName = path.substring(index + 1); + getLog().debug("Concerned property " + propertyName); + } + return false; + } + + protected void onEventInUiThread(List events) + throws RepositoryException { + if (getLog().isTraceEnabled()) + getLog().trace("Refresh result list"); + nodesViewer.refresh(); + } + + } + + /* DEPENDENCY INJECTION */ + public void setRepositoryRegister(RepositoryRegister repositoryRegister) { + this.repositoryRegister = repositoryRegister; + } + + public void setSession(Session session) { + this.session = session; + } + +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ImportFileSystemWizard.java b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ImportFileSystemWizard.java new file mode 100644 index 000000000..9e9168e4f --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/wizards/ImportFileSystemWizard.java @@ -0,0 +1,220 @@ +package org.argeo.jcr.ui.explorer.wizards; + +import java.io.File; +import java.io.FileInputStream; + +import javax.jcr.Binary; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.nodetype.NodeType; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; +import org.argeo.eclipse.ui.dialogs.Error; +import org.argeo.eclipse.ui.specific.ImportToServerWizardPage; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.wizard.Wizard; + +public class ImportFileSystemWizard extends Wizard { + private final static Log log = LogFactory + .getLog(ImportFileSystemWizard.class); + + private ImportToServerWizardPage importPage; + private final Node folder; + + public ImportFileSystemWizard(Node folder) { + this.folder = folder; + setWindowTitle("Import from file system"); + } + + @Override + public void addPages() { + importPage = new ImportToServerWizardPage(); + addPage(importPage); + setNeedsProgressMonitor(importPage.getNeedsProgressMonitor()); + } + + /** + * Called when the user click on 'Finish' in the wizard. The real upload to + * the JCR repository is done here. + */ + @Override + public boolean performFinish() { + + // Initialization + final String objectType = importPage.getObjectType(); + final String objectPath = importPage.getObjectPath(); + + // We do not display a progress bar for one file only + if (importPage.FILE_ITEM_TYPE.equals(objectType)) { + // In Rap we must force the "real" upload of the file + importPage.performFinish(); + try { + Node fileNode = folder.addNode(importPage.getObjectName(), + NodeType.NT_FILE); + Node resNode = fileNode.addNode(Property.JCR_CONTENT, + NodeType.NT_RESOURCE); + Binary binary = null; + try { + binary = folder.getSession().getValueFactory() + .createBinary(importPage.getFileInputStream()); + resNode.setProperty(Property.JCR_DATA, binary); + } finally { + if (binary != null) + binary.dispose(); + IOUtils.closeQuietly(importPage.getFileInputStream()); + } + folder.getSession().save(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } else if (importPage.FOLDER_ITEM_TYPE.equals(objectType)) { + if (objectPath == null || !new File(objectPath).exists()) { + Error.show("Directory " + objectPath + " does not exist"); + return false; + } + + Boolean failed = false; + final File dir = new File(objectPath).getAbsoluteFile(); + final Long sizeB = directorySize(dir, 0l); + final Stats stats = new Stats(); + Long begin = System.currentTimeMillis(); + try { + getContainer().run(true, true, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) { + try { + Integer sizeKB = (int) (sizeB / FileUtils.ONE_KB); + monitor.beginTask("", sizeKB); + importDirectory(folder, dir, monitor, stats); + monitor.done(); + } catch (Exception e) { + if (e instanceof RuntimeException) + throw (RuntimeException) e; + else + throw new ArgeoException("Cannot import " + + objectPath, e); + } + } + }); + } catch (Exception e) { + Error.show("Cannot import " + objectPath, e); + failed = true; + } + + Long duration = System.currentTimeMillis() - begin; + Long durationS = duration / 1000l; + String durationStr = (durationS / 60) + " min " + (durationS % 60) + + " s"; + StringBuffer message = new StringBuffer("Imported\n"); + message.append(stats.fileCount).append(" files\n"); + message.append(stats.dirCount).append(" directories\n"); + message.append(FileUtils.byteCountToDisplaySize(stats.sizeB)); + if (failed) + message.append(" of planned ").append( + FileUtils.byteCountToDisplaySize(sizeB)); + message.append("\n"); + message.append("in ").append(durationStr).append("\n"); + if (failed) + MessageDialog.openError(getShell(), "Import failed", + message.toString()); + else + MessageDialog.openInformation(getShell(), "Import successful", + message.toString()); + + return true; + } + return false; + + } + + /** Recursively computes the size of the directory in bytes. */ + protected Long directorySize(File dir, Long currentSize) { + Long size = currentSize; + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + size = directorySize(file, size); + } else { + size = size + file.length(); + } + } + return size; + } + + /** + * Import recursively a directory and its content to the repository. + */ + protected void importDirectory(Node folder, File dir, + IProgressMonitor monitor, Stats stats) { + try { + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + Node childFolder = folder.addNode(file.getName(), + NodeType.NT_FOLDER); + importDirectory(childFolder, file, monitor, stats); + folder.getSession().save(); + stats.dirCount++; + } else { + Long fileSize = file.length(); + + // we skip tempory files that are created by apps when a + // file is being edited. + // TODO : make this configurable. + if (file.getName().lastIndexOf('~') != file.getName() + .length() - 1) { + + monitor.subTask(file.getName() + " (" + + FileUtils.byteCountToDisplaySize(fileSize) + + ") " + file.getCanonicalPath()); + try { + Node fileNode = folder.addNode(file.getName(), + NodeType.NT_FILE); + Node resNode = fileNode.addNode( + Property.JCR_CONTENT, NodeType.NT_RESOURCE); + Binary binary = null; + try { + binary = folder + .getSession() + .getValueFactory() + .createBinary(new FileInputStream(file)); + resNode.setProperty(Property.JCR_DATA, binary); + } finally { + if (binary != null) + binary.dispose(); + } + folder.getSession().save(); + stats.fileCount++; + stats.sizeB = stats.sizeB + fileSize; + } catch (Exception e) { + log.warn("Import of " + + file + + " (" + + FileUtils + .byteCountToDisplaySize(fileSize) + + ") failed: " + e); + folder.getSession().refresh(false); + } + monitor.worked((int) (fileSize / FileUtils.ONE_KB)); + } + } + } + } catch (Exception e) { + throw new ArgeoException("Cannot import " + dir + " to " + folder, + e); + } + } + + static class Stats { + public Long fileCount = 0l; + public Long dirCount = 0l; + public Long sizeB = 0l; + } +} diff --git a/server/plugins/org.argeo.jcr.ui.explorer/src/main/resources/org/argeo/jcr/ui/explorer/messages.properties b/server/plugins/org.argeo.jcr.ui.explorer/src/main/resources/org/argeo/jcr/ui/explorer/messages.properties new file mode 100644 index 000000000..2902b1eae --- /dev/null +++ b/server/plugins/org.argeo.jcr.ui.explorer/src/main/resources/org/argeo/jcr/ui/explorer/messages.properties @@ -0,0 +1,8 @@ +## English labels for Agreo JCR UI application + +## Generic labels +nodeEditorLbl=Generic node editor +genericNodePageTitle=Edit Node properties + +## Dummy ones +testLbl=Internationalizations of messages seems to work properly. -- 2.39.2