// $Id$
/**
* Copyright (C) 2007 EDIT
- * European Distributed Institute of Taxonomy
+ * European Distributed Institute of Taxonomy
* http://www.e-taxonomy.eu
- *
+ *
* The contents of this file are subject to the Mozilla Public License Version 1.1
* See LICENSE.TXT at the top of this package for the full license terms.
*/
package eu.etaxonomy.taxeditor.model;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.TreeSet;
import org.apache.log4j.Logger;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;
+import eu.etaxonomy.cdm.database.PermissionDeniedException;
+import eu.etaxonomy.cdm.model.common.IEnumTerm;
+import eu.etaxonomy.cdm.persistence.hibernate.permission.SecurityExceptionUtils;
import eu.etaxonomy.taxeditor.operation.AbstractPostOperation;
import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
* <p>
* Abstract AbstractUtility class.
* </p>
- *
+ *
* @author n.hoffmann
* @created 11.05.2009
* @version 1.0
* <p>
* closeAll
* </p>
- *
+ *
* @return a boolean.
*/
public static boolean closeAll() {
/**
* Close the given editor.
- *
+ *
* @param editor
* The <tt>MultipageTaxonEditor</tt> to close.
* @return <tt>true</tt> on success
* <p>
* getShell
* </p>
- *
+ *
* @return a {@link org.eclipse.swt.widgets.Shell} object.
*/
public static Shell getShell() {
* <p>
* getActivePage
* </p>
- *
+ *
* @return a {@link org.eclipse.ui.IWorkbenchPage} object.
*/
public static IWorkbenchPage getActivePage() {
* <p>
* getActivePart
* </p>
- *
+ *
* @return a {@link org.eclipse.ui.IWorkbenchPart} object.
*/
public static IWorkbenchPart getActivePart() {
* <p>
* getWorkbenchWindow
* </p>
- *
+ *
* @return a {@link org.eclipse.jface.window.ApplicationWindow} object.
*/
public static ApplicationWindow getWorkbenchWindow() {
* <p>
* showView
* </p>
- *
+ *
* @param id
* a {@link java.lang.String} object.
* @return a {@link org.eclipse.ui.IViewPart} object.
* <p>
* hideView
* </p>
- *
+ *
* @param view
* a {@link org.eclipse.ui.IViewPart} object.
*/
* <p>
* getView
* </p>
- *
+ *
* @param id
* a {@link java.lang.String} object.
* @param restore
* <p>
* getService
* </p>
- *
+ *
* @param api
* a {@link java.lang.Class} object.
* @return a {@link java.lang.Object} object.
* <p>
* getCurrentTheme
* </p>
- *
+ *
* @return a {@link org.eclipse.ui.themes.ITheme} object.
*/
public static ITheme getCurrentTheme() {
* Fonts registered to the plugin may be obtained with the Eclipse themeing
* functionality. Thus fonts are chooseable by the user via
* Preferences->General->Appearance->Colors and Fonts
- *
+ *
* @return the FontRegistry for the current theme
*/
public static FontRegistry getFontRegistry() {
* <p>
* getFont
* </p>
- *
+ *
* @param symbolicName
* a {@link java.lang.String} object.
* @return a {@link org.eclipse.swt.graphics.Font} object.
* Color registered to the plugin may be obtained with the Eclipse themeing
* functionality. Thus colors are editable by the user via
* Preferences->General->Appearance->Colors and Fonts
- *
+ *
* @return the ColorRegistry for the current theme
*/
public static ColorRegistry getColorRegistry() {
* <p>
* getColor
* </p>
- *
+ *
* @param symbolicName
* a {@link java.lang.String} object.
* @return a {@link org.eclipse.swt.graphics.Color} object.
/**
* Open a message box that informs the user about unimplemented
* functionality. This method is for developer convenience.
- *
+ *
* @param source
* a {@link java.lang.Object} object.
*/
* <p>
* informationDialog
* </p>
- *
+ *
* @param title
* a {@link java.lang.String} object.
* @param message
final String message) {
Display.getDefault().asyncExec(new Runnable() {
- public void run() {
+ @Override
+ public void run() {
MessageDialog.openInformation(getShell(), title, message);
}
});
}
+ public static void informationDialog(final String title,
+ final IStatus status) {
+ informationDialog(title, status.getMessage());
+ }
+
/**
* <p>
* warningDialog
* </p>
- *
+ *
* @param title
* The dialogs title
* @param source
final String message) {
Display.getDefault().asyncExec(new Runnable() {
- public void run() {
+ @Override
+ public void run() {
MessageDialog.openWarning(getShell(), title, message);
Class<? extends Object> clazz = source != null ? source
.getClass() : AbstractUtility.class;
});
}
+ /**
+ * @param title
+ * @param termBase
+ * @param status
+ */
+ public static void warningDialog(String title, Object source,
+ IStatus status) {
+ warningDialog(title, source, status.getMessage());
+ }
+
/**
* <p>
* errorDialog
* </p>
- *
+ *
* @param title
* The dialogs title
* @param source
final String message, final Throwable t) {
Display.getDefault().asyncExec(new Runnable() {
- public void run() {
+ @Override
+ public void run() {
MessageDialog.openError(getShell(), title, message + getCauseRecursively(t));
Class<? extends Object> clazz = source != null ? source
.getClass() : this.getClass();
private String getCauseRecursively(Throwable t) {
if(t == null){
- return null;
+ return "";
}
-
+
if(t.getCause() != null){
return getCauseRecursively(t.getCause());
}else{
});
}
+ public static void errorDialog(final String title, final Object source,
+ final String message){
+ errorDialog(title, source, message, null);
+ }
+
/**
* <p>
* errorDialog
* </p>
- *
+ *
* @param title
* a {@link java.lang.String} object.
* @param source
* @param status
* a {@link org.eclipse.core.runtime.IStatus} object.
*/
- public static void errorDialog(final String title, final Object source,
+ public static void errorDialog(final String title, final Object source,
final IStatus status) {
Display.getDefault().asyncExec(new Runnable() {
- public void run() {
- MessageDialog.openError(getShell(), title, status.getMessage());
+ @Override
+ public void run() {
+ ErrorDialog.openError(getShell(), title, null, status);
Class<? extends Object> clazz = source != null ? source
.getClass() : this.getClass();
- error(clazz, status.getMessage(), status.getException());
+ error(clazz, status);
}
});
}
* <p>
* confirmDialog
* </p>
- *
+ *
* @param title
* a {@link java.lang.String} object.
* @param message
* <p>
* executeOperation
* </p>
- *
+ *
* @param operation
* a
- * {@link eu.etaxonomy.taxeditor.operation.AbstractPostOperation}
+ * {@link eu.etaxonomy.taxeditor.operation.AbstractPostTaxonOperation}
* object.
* @return a {@link org.eclipse.core.runtime.IStatus} object.
*/
IRunnableWithProgress runnable = new IRunnableWithProgress() {
- public void run(IProgressMonitor monitor)
+ @Override
+ public void run(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
monitor.beginTask(operation.getLabel(), 100);
IStatus status = Status.CANCEL_STATUS;
status = getOperationHistory().execute(operation, monitor,
uiInfoAdapter);
} catch (ExecutionException e) {
- errorDialog("Error executing operation", getClass(), String.format("An error occured while executing %s.", operation.getLabel()), e);
+ MultiStatus info = null;
+ String title = null;
+
+ // FIXME cannot access TaxonomicEditorPlugin.PLUGIN_ID from here
+ // String PID = TaxonomicEditorPlugin.PLUGIN_ID;
+ String PID = "eu.etaxonomy.taxeditor.application";
+
+ RuntimeException securityRuntimeException = SecurityExceptionUtils.findSecurityRuntimeException(e);
+
+ if(securityRuntimeException != null){
+ title = "Your changes could not be saved!";
+ info = new MultiStatus(PID, 1, String.format("You are missing sufficient permissions for the operation \"%s\".", operation.getLabel()), null);
+ info.add(new Status(IStatus.WARNING, PID, 1, securityRuntimeException.getMessage(), null));
+
+ } else {
+ title = "Error executing operation";
+ info = new MultiStatus(PID, 1, String.format("An error occured while executing %s.", operation.getLabel()), null);
+ Writer writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+ e.printStackTrace(printWriter);
+ info.add(new Status(IStatus.ERROR, PID, 1, writer.toString(), null));
+ }
+
+ errorDialog(title, getClass(), info);
+
} finally {
monitor.done();
}
-
+
String statusString = status.equals(Status.OK_STATUS) ? "completed"
: "cancelled";
setStatusLine(operation.getLabel() + " " + statusString + ".");
* <p>
* getOperationHistory
* </p>
- *
+ *
* @return a {@link org.eclipse.core.commands.operations.IOperationHistory}
* object.
*/
* <p>
* setStatusLine
* </p>
- *
+ *
* @param message
* a {@link java.lang.String} object.
*/
public static void setStatusLine(final String message) {
Display.getDefault().asyncExec(new Runnable() {
- public void run() {
+ @Override
+ public void run() {
statusLineManager.setMessage(message);
}
* <p>
* getMonitor
* </p>
- *
+ *
* @return a {@link org.eclipse.core.runtime.IProgressMonitor} object.
*/
public static IProgressMonitor getMonitor() {
/**
* Starts either the given {@link IProgressMonitor} if it's not
* <code>null</code> or a new {@link NullProgressMonitor}.
- *
+ *
* @param progressMonitor
* The {@link IProgressMonitor} or <code>null</code> if no
* progress should be reported.
* Creates a {@link SubProgressMonitor} if the given
* {@link IProgressMonitor} is not <code>null</code> and not a
* {@link NullProgressMonitor}.
- *
+ *
* @param progressMonitor
* The parent {@link IProgressMonitor} of the
* {@link SubProgressMonitor} to be created.
/**
* Checks whether the user canceled this operation. If not canceled, the
* given number of steps are declared as done.
- *
+ *
* @param newMonitor
* a {@link org.eclipse.core.runtime.IProgressMonitor} object.
* @param steps
/**
* Present a progress dialog to the user. This dialog will block the UI
- *
+ *
* @param runnable
* an implementation of {@link IRunnableWithProgress}
* @throws java.lang.InterruptedException
* <p>
* runInUI
* </p>
- *
+ *
* @see {@link IProgressService#runInUI(org.eclipse.jface.operation.IRunnableContext, IRunnableWithProgress, ISchedulingRule)}
* @param runnable
* a {@link org.eclipse.jface.operation.IRunnableWithProgress}
* <p>
* run
* </p>
- *
+ *
* @param fork
* a boolean.
* @param cancelable
* <p>
* getProgressService
* </p>
- *
+ *
* @return a {@link org.eclipse.ui.progress.IProgressService} object.
*/
public static IProgressService getProgressService() {
* <p>
* getProgressService2
* </p>
- *
+ *
* @return a {@link org.eclipse.ui.progress.IWorkbenchSiteProgressService}
* object.
*/
* <p>
* info
* </p>
- *
+ *
* @param message
* a {@link java.lang.String} object.
*/
* <p>
* info
* </p>
- *
+ *
* @param status
* a {@link org.eclipse.core.runtime.IStatus} object.
*/
* <p>
* warn
* </p>
- *
+ *
* @param source
* a {@link java.lang.Class} object.
* @param message
getLog4JLogger(source).warn(message);
log(status);
}
-
+
+ public static void warn(Class source, IStatus status) {
+ getLog4JLogger(source).warn(status.getMessage(), status.getException());
+ log(status);
+ }
+
public static void warn(Class source, Throwable t) {
IStatus status = new Status(IStatus.WARNING, getPluginId(), t.getMessage(), t);
getLog4JLogger(source).warn(t);
* <p>
* error
* </p>
- *
+ *
* @param source
* a {@link java.lang.Class} object.
* @param t
* <p>
* error
* </p>
- *
+ *
* @param source
* a {@link java.lang.Class} object.
* @param message
* <p>
* error
* </p>
- *
+ *
* @param source
* a {@link java.lang.Class} object.
* @param status
* <p>
* getLog4JLogger
* </p>
- *
+ *
* @param clazz
* a {@link java.lang.Class} object.
* @return a {@link org.apache.log4j.Logger} object.
/**
* @see {@link ILog#log(IStatus)}
- *
+ *
* @param status
*/
private static void log(IStatus status) {
* <p>
* getPluginId
* </p>
- *
+ *
* @return a {@link java.lang.String} object.
*/
- protected static String getPluginId() {
+ public static String getPluginId() {
return "eu.taxeditor";
}
* <p>
* getActiveEditor
* </p>
- *
+ *
* @return a {@link org.eclipse.ui.IEditorPart} object.
*/
public static IEditorPart getActiveEditor() {
* <p>
* getDetailsView
* </p>
- *
+ *
* @return a {@link eu.etaxonomy.taxeditor.view.detail.DetailsViewPart}
* object.
*/
.reflow();
}
}
+
+
+ /**
+ * Orders a Collection of {@link IEnumTerm}s according to the term
+ * hierarchy. <br>
+ * <br>
+ * The returned map will be be ordered primarily by root elements,
+ * secondarily by the child elements and their children resp., both ascending alphabetically. <br>
+ * @param terms
+ * A {@link Collection} of {@link IEnumTerm}s for which the term
+ * hierarchy should be created
+ * @return a map which holds the terms as keys and their string
+ * representation via {@link IEnumTerm#getMessage()} as values
+ */
+ public static <T extends IEnumTerm<T>> LinkedHashMap<T, String> orderTerms(Collection<T> terms) {
+ TreeSet<TermNode<T>> parentElements = new TreeSet<TermNode<T>>();
+ parentElements.addAll(getTermHierarchy(terms));
+
+ // create list according to the type hierarchy (root elements alphabetically with recursive children also alphabetically)
+ LinkedHashMap<T, String> result = new LinkedHashMap<T, String>();
+ parseTermTree(parentElements, result, -1);
+ return result;
+ }
+
+ private static<T extends IEnumTerm<T>> void parseTermTree(Collection<TermNode<T>> children, LinkedHashMap<T, String> result, int depth){
+ depth++;
+ for(TermNode<T> node:children){
+ String indentString = "";
+ for(int i=0;i<depth;i++){
+ indentString += " ";
+ }
+ if(depth>0){
+ indentString += "- ";
+ }
+ result.put(node.term, indentString + node.term.getMessage());
+ parseTermTree(node.children, result, depth);
+ }
+ }
+
+ private static<T extends IEnumTerm<T>> void addToParents(List<TermNode<T>> parents, Collection<T> terms){
+ List<TermNode<T>> hasChildrenList = new ArrayList<TermNode<T>>();
+ for(T term:terms){
+ // only terms with parents
+ if(term.getKindOf()!=null){
+ TermNode<T> parentNode = new TermNode<T>(term.getKindOf());
+ TermNode<T> childNode = new TermNode<T>(term);
+ if(parents.contains(parentNode)){
+ // parent found in parent list -> add this term to parent's child list
+ parents.get(parents.indexOf(parentNode)).addChild(childNode);
+ if(!term.getGeneralizationOf().isEmpty()){
+ // has more children -> add to list which will be the parent for the next recursion
+ hasChildrenList.add(childNode);
+ }
+ }
+ }
+ }
+ if(!hasChildrenList.isEmpty()){
+ addToParents(hasChildrenList, terms);
+ }
+ }
+
+ private static<T extends IEnumTerm<T>> List<TermNode<T>> getTermHierarchy(Collection<T> terms){
+ List<TermNode<T>> parents = new ArrayList<TermNode<T>>();
+ // get root elements
+ for(T term:terms){
+ T parentTerm = term.getKindOf();
+ if(parentTerm==null){
+ // root element
+ parents.add(new TermNode<T>(term));
+ }
+ }
+ addToParents(parents, terms);
+ return parents;
+ }
+
+ @SuppressWarnings("unchecked")
+ /**
+ * Recursively iterates over all term parents until no more parent is found i.e. the root node
+ * @param term The term for which the parent should be found
+ * @return the root terms of the term hierarchy
+ */
+ private static<T extends IEnumTerm<T>> T getParentFor(T term){
+ // PP: cast should be safe. Why is Eclipse complaining??
+ T parent = term.getKindOf();
+ if(parent==null){
+ return term;
+ }
+ else{
+ return getParentFor(term.getKindOf());
+ }
+ }
+
+ private static class TermNode<T extends IEnumTerm<T>> implements Comparable<TermNode<T>>{
+ private final T term;
+ private final TreeSet<TermNode<T>> children;
+
+ /**
+ * @param term
+ * @param children
+ */
+ public TermNode(T term) {
+ super();
+ this.term = term;
+ this.children = new TreeSet<TermNode<T>>();
+ }
+
+ public void addChild(TermNode<T> child){
+ this.children.add(child);
+ }
+
+ /**
+ * @return the children
+ */
+ public TreeSet<TermNode<T>> getChildren() {
+ return children;
+ }
+
+ /**
+ * @return the term
+ */
+ public T getTerm() {
+ return term;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((term == null) ? 0 : term.hashCode());
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ TermNode other = (TermNode) obj;
+ if (term == null) {
+ if (other.term != null) {
+ return false;
+ }
+ } else if (!term.equals(other.term)) {
+ return false;
+ }
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ @Override
+ public int compareTo(TermNode<T> that) {
+ return this.term.getMessage().compareTo(that.term.getMessage());
+ }
+ }
+
}