/*
* Copyright 2010 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.guvnor.tools.views;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;
import org.guvnor.tools.Activator;
import org.guvnor.tools.GuvnorRepository;
import org.guvnor.tools.Messages;
import org.guvnor.tools.GuvnorLocationManager.IRepositorySetListener;
import org.guvnor.tools.utils.GuvnorMetadataProps;
import org.guvnor.tools.utils.GuvnorMetadataUtils;
import org.guvnor.tools.utils.PlatformUtils;
import org.guvnor.tools.utils.webdav.IWebDavClient;
import org.guvnor.tools.utils.webdav.ResourceProperties;
import org.guvnor.tools.utils.webdav.WebDavClientFactory;
import org.guvnor.tools.utils.webdav.WebDavServerCache;
import org.guvnor.tools.views.model.TreeObject;
import org.guvnor.tools.views.model.TreeParent;
import org.guvnor.tools.wizards.NewRepLocationWizard;
/**
* A view showing Guvnor structure in a tree.
*/
public class RepositoryView extends ViewPart {
private TreeViewer viewer;
private DrillDownAdapter drillDownAdapter;
private Action deleteRepositoryLocAction;
private Action addRepositoryLocAction;
private Action doubleClickAction;
private Action refreshAction;
class NameSorter extends ViewerSorter {
}
public RepositoryView() {
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
public void createPartControl(Composite parent) {
viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
drillDownAdapter = new DrillDownAdapter(viewer);
viewer.setContentProvider(new RepositoryContentProvider());
viewer.setLabelProvider(new RepositoryLabelProvider());
viewer.setSorter(new NameSorter());
viewer.setInput(viewer);
makeActions();
hookContextMenu();
hookDoubleClickAction();
contributeToActionBars();
Activator.getLocationManager().addRepositorySetListener(new IRepositorySetListener() {
public void repositorySetChanged(int type, List<GuvnorRepository> repList) {
// TODO: Just creating an entirely new content provider.
// Someday might update this to have incremental changes
// to existing content provider.
viewer.setContentProvider(new RepositoryContentProvider());
}
});
super.getSite().setSelectionProvider(viewer);
addDragDropSupport();
}
public void refresh() {
viewer.refresh();
}
private void addDragDropSupport() {
// TODO: Support drag and drop of directories
Transfer[] transfers = new Transfer[] { FileTransfer.getInstance()};
viewer.addDragSupport(DND.DROP_COPY, transfers, new DragSourceListener() {
private TreeObject[] target;
public void dragFinished(DragSourceEvent event) {
target = null;
event.doit = true;
}
public void dragSetData(DragSourceEvent event) {
if (target == null) {
return;
}
try {
List<String> files = prepareFileTransfer(target);
String[] res = new String[files.size()];
files.toArray(res);
event.data = res;
} catch (Exception e) {
// Note: we could catch a 401 (not authorized) WebDav error and retry,
// like we do when listing directories. But since we have to first list
// directories to get to files, and the act of listing directories authenticates
// for the server, currently we do not have a situation requiring authentication
// for specific files. This might be different in the future if the Guvnor security
// model changes, or users can directly connect to specific files.
Activator.getDefault().displayError(IStatus.ERROR, e.getMessage(), e, true);
}
}
public void dragStart(DragSourceEvent event) {
List<TreeObject> transferNodes = new ArrayList<TreeObject>();
ISelection selection = viewer.getSelection();
Object[] objs = ((IStructuredSelection)selection).toArray();
for (int i = 0; i < objs.length; i++) {
if (objs[i] instanceof TreeObject
&& ((TreeObject)objs[i]).getNodeType() == TreeObject.Type.RESOURCE) {
transferNodes.add((TreeObject)objs[i]);
}
}
if (transferNodes.size() > 0) {
event.doit = true;
target = new TreeObject[transferNodes.size()];
transferNodes.toArray(target);
} else {
event.doit = false;
}
}
});
viewer.addDropSupport(DND.DROP_COPY | DND.DROP_MOVE, transfers, new ViewerDropAdapter(viewer) {
private TreeParent targetNode;
@Override
public boolean performDrop(Object data) {
if (targetNode == null) {
return false;
}
String[] items = (String[])data;
boolean isvalid = validateInputResource(items);
if(!isvalid) return false;
String[] errors = processDrop(targetNode, items);
if (errors.length != 0) {
StringBuilder msg = new StringBuilder();
for (int i = 0; i < errors.length; i++) {
msg.append(errors[i]);
msg.append("\r\n"); //$NON-NLS-1$
}
Activator.getDefault().
displayError(IStatus.ERROR, msg.toString(), new Exception(), true);
}
if (items.length != errors.length) {
// At least one item did not have an error,
// so refresh the views
PlatformUtils.updateDecoration();
PlatformUtils.refreshRepositoryView();
}
return items.length != errors.length;
}
@Override
public boolean validateDrop(Object target,
int operation,
TransferData transferType) {
// The drop target needs to be a directory
if (target == null
|| !(target instanceof TreeParent)) {
targetNode = null;
return false;
}
targetNode = (TreeParent)target;
return true;
}
});
}
private boolean validateInputResource(String[] filenames){
for(String filename : filenames){
File file = new File(filename);
String msg = "";
if(!file.isFile()){
msg = MessageFormat.format(Messages.getString("error.message.isfolder"), filename);
MessageDialog.openError(null, "Problem Occurred", msg);
return false;
}
String name = file.getName();
if(!(name.indexOf(".") > 0)){
msg = MessageFormat.format(Messages.getString("error.message.invalid.filename"), filename);
MessageDialog.openError(null, "Problem Occurred", msg);
return false;
}
}
return true;
}
private String[] processDrop(TreeParent target, String[] items) {
List<String> errors = new ArrayList<String>();
for (int i = 0; i < items.length; i++) {
IFile sourceFile = PlatformUtils.getResourceFromFSPath(items[i]);
if (sourceFile != null) {
try {
GuvnorMetadataProps md = GuvnorMetadataUtils.getGuvnorMetadata(sourceFile);
if (md == null) {
// The file is not already associated with Guvnor, so just add it
boolean res = GuvnorMetadataUtils.
addResourceToGuvnor(target.getGuvnorRepository().getLocation(),
target.getFullPath(), sourceFile);
if (!res) {
errors.add(MessageFormat.format(Messages.getString("add.failure.exist"), //$NON-NLS-1$
new Object[] {sourceFile.getName(), target.getFullPath()}));
}
} else {
// Need to check if the drop location is the same as the Guvnor
// associated location. If so, then perform commit.
// If not, disallow the drop.
String itemPath = target.getFullPath() + sourceFile.getName();
if (itemPath.equals(md.getFullpath())) {
// If there are pending changes
if (!GuvnorMetadataUtils.isGuvnorResourceCurrent(sourceFile)) {
GuvnorMetadataUtils.commitFileChanges(sourceFile);
}
} else {
errors.add(MessageFormat.format(Messages.getString("already.guvnor.as"), //$NON-NLS-1$
new Object[] { items[i],
md.getFullpath() }));
}
}
} catch (Exception e) {
Activator.getDefault().writeLog(IStatus.ERROR, e.getMessage(), e);
}
} else {
Activator.getDefault().
writeLog(IStatus.WARNING,
"Could not resolve: " + items[i], //$NON-NLS-1$
new Exception());
}
}
String[] res = new String[errors.size()];
errors.toArray(res);
return res;
}
private List<String> prepareFileTransfer(TreeObject[] nodes) throws Exception {
List<String> res = new ArrayList<String>();
for (int i = 0; i < nodes.length; i++) {
String contents = getResourceContents(nodes[i]);
IPath path = new Path(Activator.getDefault().getStateLocation().toOSString() +
File.separator + "t" + //$NON-NLS-1$
String.valueOf(System.currentTimeMillis()));
if (!path.toFile().mkdir()) {
throw new Exception("Could not create directory " + path.toOSString()); //$NON-NLS-1$
}
path.toFile().deleteOnExit();
File transfer = new File(path + File.separator + nodes[i].getName());
transfer.deleteOnExit();
FileOutputStream fos = new FileOutputStream(transfer);
PrintWriter writer = new PrintWriter(fos);
writer.write(contents);
writer.flush();
writer.close();
res.add(transfer.getAbsolutePath());
IPath metaPath = GuvnorMetadataUtils.
createGuvnorMetadataLocation(path.toOSString());
metaPath.toFile().deleteOnExit();
File metaFile = GuvnorMetadataUtils.
getGuvnorMetadataFile(metaPath.toOSString(), nodes[i].getName());
metaFile.deleteOnExit();
GuvnorMetadataUtils.writeGuvnorMetadataProps(metaFile, getGuvnorMetadataProps(nodes[i]));
res.add(metaFile.getAbsolutePath());
// Put at least a 1ms gap in generated dir names above
Thread.sleep(1, 0);
}
return res;
}
private GuvnorMetadataProps getGuvnorMetadataProps(TreeObject node) throws Exception {
GuvnorRepository rep = node.getGuvnorRepository();
IWebDavClient webdav = WebDavServerCache.getWebDavClient(rep.getLocation());
if (webdav == null) {
webdav = WebDavClientFactory.createClient(new URL(rep.getLocation()));
WebDavServerCache.cacheWebDavClient(rep.getLocation(), webdav);
}
ResourceProperties props = webdav.queryProperties(node.getFullPath());
return new GuvnorMetadataProps(node.getName(),
node.getGuvnorRepository().getLocation(),
node.getFullPath(),
props.getLastModifiedDate(),
props.getRevision());
}
private void hookContextMenu() {
MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
RepositoryView.this.fillContextMenu(manager);
}
});
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
getSite().registerContextMenu(menuMgr, viewer);
}
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalPullDown(bars.getMenuManager());
fillLocalToolBar(bars.getToolBarManager());
}
private void fillLocalPullDown(IMenuManager manager) {
manager.add(deleteRepositoryLocAction);
manager.add(new Separator());
manager.add(addRepositoryLocAction);
manager.add(new Separator());
manager.add(refreshAction);
}
private boolean shouldAddDeleteAction() {
ISelection selection = viewer.getSelection();
if (selection == null) {
return false;
}
Object obj = ((IStructuredSelection)selection).getFirstElement();
if (obj instanceof TreeParent) {
return ((TreeParent)obj).getNodeType() == TreeObject.Type.REPOSITORY;
} else {
return false;
}
}
private void fillContextMenu(IMenuManager manager) {
if (shouldAddDeleteAction()) {
manager.add(deleteRepositoryLocAction);
}
manager.add(refreshAction);
drillDownAdapter.addNavigationActions(manager);
// Other plug-ins can contribute there actions here
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
private void fillLocalToolBar(IToolBarManager manager) {
manager.add(deleteRepositoryLocAction);
manager.add(addRepositoryLocAction);
manager.add(new Separator());
drillDownAdapter.addNavigationActions(manager);
}
private void makeActions() {
deleteRepositoryLocAction = new Action() {
public void run() {
ISelection selection = viewer.getSelection();
Object obj = ((IStructuredSelection)selection).getFirstElement();
if (obj instanceof TreeParent) {
GuvnorRepository rep = ((TreeParent)obj).getGuvnorRepository();
if (MessageDialog.openConfirm(RepositoryView.this.getSite().getShell(),
Messages.getString("remove.rep.dialog.caption"), //$NON-NLS-1$
MessageFormat.format(Messages.getString("remove.rep.dialog.msg"), //$NON-NLS-1$
new Object[] { rep.getLocation() }))) {
Activator.getLocationManager().removeRepository(rep.getLocation());
}
}
}
};
deleteRepositoryLocAction.setText(Messages.getString("action.delete.rep")); //$NON-NLS-1$
deleteRepositoryLocAction.setToolTipText(Messages.getString("action.delete.rep.desc")); //$NON-NLS-1$
deleteRepositoryLocAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
addRepositoryLocAction = new Action() {
public void run() {
NewRepLocationWizard wiz = new NewRepLocationWizard();
wiz.init(Activator.getDefault().getWorkbench(), null);
WizardDialog dialog =
new WizardDialog(RepositoryView.this.getSite().getShell(), wiz);
dialog.create();
dialog.open();
}
};
addRepositoryLocAction.setText(Messages.getString("action.add.rep")); //$NON-NLS-1$
addRepositoryLocAction.setToolTipText(Messages.getString("action.add.rep.desc")); //$NON-NLS-1$
addRepositoryLocAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(ISharedImages.IMG_TOOL_NEW_WIZARD));
doubleClickAction = new Action() {
public void run() {
ISelection selection = viewer.getSelection();
Object obj = ((IStructuredSelection)selection).getFirstElement();
if (obj instanceof TreeObject) {
doubleClick((TreeObject)obj);
}
}
};
refreshAction = new Action() {
public void run() {
ISelection selection = viewer.getSelection();
if (selection instanceof IStructuredSelection) {
IStructuredSelection strucSel = (IStructuredSelection)selection;
if (!strucSel.isEmpty()) {
viewer.refresh(strucSel.getFirstElement());
}
}
}
};
refreshAction.setText(Messages.getString("action.refresh.rep")); //$NON-NLS-1$
refreshAction.setToolTipText(Messages.getString("action.refresh.rep.desc")); //$NON-NLS-1$
refreshAction.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
getImageDescriptor(ISharedImages.IMG_TOOL_REDO));
}
private void hookDoubleClickAction() {
viewer.addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
doubleClickAction.run();
}
});
}
private void doubleClick(TreeObject node) {
if (node instanceof TreeParent) {
if (viewer.getExpandedState(node)) {
viewer.collapseToLevel(node, 1);
} else {
viewer.expandToLevel(node, 1);
}
} else {
try {
String contents = getResourceContents(node);
PlatformUtils.openEditor(contents, node.getName());
} catch (Exception e) {
// Note: we could catch a 401 (not authorized) WebDav error and retry,
// like we do when listing directories. But since we have to first list
// directories to get to files, and the act of listing directories authenticates
// for the server, currently we do not have a situation requiring authentication
// for specific files. This might be different in the future if the Guvnor security
// model changes, or users can directly connect to specific files.
Activator.getDefault().displayError(IStatus.ERROR, e.getMessage(), e, true);
}
}
}
private String getResourceContents(TreeObject node) throws Exception {
GuvnorRepository rep = node.getGuvnorRepository();
IWebDavClient webdav = WebDavServerCache.getWebDavClient(rep.getLocation());
if (webdav == null) {
webdav = WebDavClientFactory.createClient(new URL(rep.getLocation()));
WebDavServerCache.cacheWebDavClient(rep.getLocation(), webdav);
}
String res = webdav.getResourceContents(node.getFullPath());
return res;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.part.WorkbenchPart#setFocus()
*/
public void setFocus() {
viewer.getControl().setFocus();
}
}