package com.dubture.composer.ui.editor.composer;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.IMessage;
import org.eclipse.ui.forms.editor.SharedHeaderFormEditor;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.sourceforge.jsonedit.core.editors.JsonTextEditor;
import com.dubture.composer.core.ComposerPlugin;
import com.dubture.composer.core.ComposerPreferenceConstants;
import com.dubture.composer.core.buildpath.BuildPathManager;
import com.dubture.composer.core.log.Logger;
import com.dubture.composer.core.preferences.PreferencesSupport;
import com.dubture.composer.core.resources.IComposerProject;
import com.dubture.composer.internal.core.resources.ComposerProject;
import com.dubture.composer.ui.actions.InstallAction;
import com.dubture.composer.ui.actions.InstallDevAction;
import com.dubture.composer.ui.actions.SelfUpdateAction;
import com.dubture.composer.ui.actions.UpdateDevAction;
import com.dubture.composer.ui.actions.UpdateAction;
import com.dubture.composer.ui.editor.ComposerFormPage;
import com.dubture.composer.ui.job.ComposerJob;
import com.dubture.composer.ui.job.UpdateDevJob;
import com.dubture.composer.ui.job.UpdateJob;
import com.dubture.getcomposer.core.ComposerPackage;
import com.dubture.getcomposer.core.VersionedPackage;
import com.dubture.getcomposer.core.collection.ComposerPackages;
import com.dubture.getcomposer.json.ParseException;
public class ComposerFormEditor extends SharedHeaderFormEditor {
public static final String ID = "com.dubture.composer.ui.editor.composer.ComposerEditor";
public static final String MSG_PARSE_ERROR = "com.dubture.composer.ui.editor.composer.ParseException";
protected boolean dirty = false;
protected ComposerPackage composerPackage = null;
protected IDocumentProvider documentProvider;
private ISharedImages sharedImages = null;
private IProject project;
private IComposerProject composerProject;
private BuildPathManager buildPathManager;
private IAction installAction = null;
private IAction installDevAction = null;
private IAction updateAction = null;
private IAction updateNoDevAction = null;
private IAction selfUpdateAction = null;
private int jsonEditorIndex;
private int lastPageIndex = -1;
protected OverviewPage overviewPage;
protected DependenciesPage dependenciesPage;
protected ConfigurationPage configurationPage;
protected AutoloadPage autoloadPage;
protected JsonTextEditor jsonEditor;
protected DependencyGraphPage graphPage;
protected IToolBarManager toolbarManager;
private IFile jsonFile;
private String jsonDump = null;
private boolean saving = false;
private boolean pageChanging = false;
private boolean validJson = true;
private boolean newDepSinceLastSave = false;
private boolean newDevDepSinceLastSave = false;
public ComposerFormEditor() {
super();
jsonEditor = new JsonTextEditor();
documentProvider = jsonEditor.getDocumentProvider();
}
@Override
protected void setInput(IEditorInput input) {
super.setInput(input);
try {
documentProvider.connect(input);
documentProvider.getDocument(getEditorInput()).addDocumentListener(new IDocumentListener() {
@Override
public void documentChanged(DocumentEvent event) {
ComposerFormEditor.this.documentChanged(event);
}
@Override
public void documentAboutToBeChanged(DocumentEvent event) {
ComposerFormEditor.this.documentAboutToBeChanged(event);
}
});
} catch (CoreException e) {
Logger.logException(e);
}
}
@Override
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
super.init(site, input);
if (input instanceof IFileEditorInput) {
jsonFile = ((IFileEditorInput)input).getFile();
if (jsonFile != null) {
project = jsonFile.getProject();
composerProject = new ComposerProject(project);
buildPathManager = new BuildPathManager(composerProject);
setPartName(project.getName());
ResourcesPlugin.getWorkspace().addResourceChangeListener(new IResourceChangeListener() {
@Override
public void resourceChanged(IResourceChangeEvent event) {
ComposerFormEditor.this.resourceChanged(event);
}
});
}
}
composerPackage = new ComposerPackage();
composerPackage.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
Logger.debug("Property change: " + e.getPropertyName() + ", oldValue: " + e.getOldValue() + ", newValue: " + e.getNewValue());
if (e.getPropertyName().startsWith("require")) {
newDepSinceLastSave = true;
}
if (e.getPropertyName().startsWith("require-dev")) {
newDevDepSinceLastSave = true;
}
setDirty(true);
}
});
}
@Override
protected void createHeaderContents(IManagedForm headerForm) {
super.createHeaderContents(headerForm);
ScrolledForm header = headerForm.getForm();
header.setText("Composer");
FormToolkit toolkit = headerForm.getToolkit();
toolkit.decorateFormHeading(header.getForm());
toolbarManager = (ToolBarManager) header.getToolBarManager();
}
@Override
protected void createPages() {
// create pages
overviewPage = new OverviewPage(this, OverviewPage.ID, "Overview");
dependenciesPage = new DependenciesPage(this, DependenciesPage.ID, "Dependencies");
configurationPage = new ConfigurationPage(this, ConfigurationPage.ID, "Configuration");
autoloadPage = new AutoloadPage(this, AutoloadPage.ID, "Autoload");
graphPage = new DependencyGraphPage(this, DependencyGraphPage.ID, "Dependency Graph");
// add them
super.createPages();
// contribute toolbar items
for (Object pageObj : pages) {
if (pageObj instanceof ComposerFormPage && pageObj != jsonEditor) {
ComposerFormPage page = (ComposerFormPage) pageObj;
page.contributeToToolbar(toolbarManager, getHeaderForm());
}
}
contributeToToolbar(toolbarManager);
// parse json
jsonDump = documentProvider.getDocument(getEditorInput()).get();
parse(jsonDump);
setDirty(false);
newDepSinceLastSave = false;
newDevDepSinceLastSave = false;
if (!validJson) {
setActivePage(jsonEditorIndex);
IManagedForm headerForm = getHeaderForm();
if (headerForm != null) {
headerForm.getMessageManager().update();
}
}
}
@Override
protected void addPages() {
try {
addPage(overviewPage);
addPage(dependenciesPage);
addPage(autoloadPage);
addPage(configurationPage);
addPage(graphPage);
jsonEditorIndex = addPage(jsonEditor, getEditorInput());
setPageText(jsonEditorIndex, jsonEditor.getTitle());
} catch (PartInitException e) {
Logger.logException(e);
}
}
@Override
protected void pageChange(int newPageIndex) {
// change page first
super.pageChange(newPageIndex);
pageChanging = true;
// change to json editor
if (isJsonEditor()) {
if (validJson) {
IDocument document = documentProvider.getDocument(getEditorInput());
String json = composerPackage.toJson();
document.set(json);
setDirty(jsonDump != null && !jsonDump.equals(json));
jsonDump = json;
}
getHeaderForm().getForm().setText(jsonEditor.getTitle());
}
// change from json editor
if (lastPageIndex != -1 && lastPageIndex == jsonEditorIndex) {
String json = documentProvider.getDocument(jsonEditor.getEditorInput()).get();
if (jsonDump != null && !jsonDump.equals(json)) {
boolean dirty = isDirty();
parse(json);
setDirty(dirty);
}
}
lastPageIndex = newPageIndex;
pageChanging = false;
}
protected void contributeToToolbar(IToolBarManager manager) {
// this does not work for some reasons? how to make it working and get rid of the action package?
// IMenuService menuService = (IMenuService) getSite().getService(IMenuService.class);
// menuService.populateContributionManager(manager, "toolbar:com.dubture.composer.ui.editor.toolbar");
manager.add(getInstallAction());
manager.add(getInstallDevAction());
manager.add(new Separator());
manager.add(getUpdateNoDevAction());
manager.add(getUpdateAction());
manager.add(new Separator());
manager.add(getSelfUpdateAction());
manager.update(true);
}
@Override
public void dispose() {
toolbarManager = null;
super.dispose();
}
protected ISharedImages getSharedImages() {
if (sharedImages == null) {
getSite().getPage().getWorkbenchWindow().getWorkbench().getSharedImages();
}
return sharedImages;
}
protected IAction getInstallAction() {
if (installAction == null) {
installAction = new InstallAction(project, getSite());
}
return installAction;
}
protected IAction getInstallDevAction() {
if (installDevAction == null) {
installDevAction = new InstallDevAction(project, getSite());
}
return installDevAction;
}
protected IAction getUpdateAction() {
if (updateAction == null) {
updateAction = new UpdateDevAction(project, getSite());
}
return updateAction;
}
protected IAction getUpdateNoDevAction() {
if (updateNoDevAction == null) {
updateNoDevAction = new UpdateAction(project, getSite());
}
return updateNoDevAction;
}
protected IAction getSelfUpdateAction() {
if (selfUpdateAction == null) {
selfUpdateAction = new SelfUpdateAction(project, getSite());
}
return selfUpdateAction;
}
public void doSave(IProgressMonitor monitor) {
try {
saving = true;
IDocument document = documentProvider.getDocument(getEditorInput());
if (isJsonEditor()) {
parse(document.get());
} else {
validateJson(document.get());
}
if (!isJsonEditor() && isValidJson()) {
document.set(composerPackage.toJson());
}
// write
documentProvider.aboutToChange(getEditorInput());
documentProvider.saveDocument(monitor, getEditorInput(), document, true);
documentProvider.changed(getEditorInput());
jsonDump = document.get();
setDirty(false);
saving = false;
// save actions
IPreferenceStore store = ComposerPlugin.getDefault().getPreferenceStore();
PreferencesSupport prefSupport = new PreferencesSupport(ComposerPlugin.ID, store);
Boolean buildpath = prefSupport.getBooleanPreferencesValue(ComposerPreferenceConstants.SAVEACTION_BUILDPATH, false, project);
Boolean update = prefSupport.getBooleanPreferencesValue(ComposerPreferenceConstants.SAVEACTION_UPDATE, false, project);
update = update && (newDepSinceLastSave || newDevDepSinceLastSave);
if (update) {
ComposerJob job;
if (newDevDepSinceLastSave || hasDevDepsInstalled()) {
job = new UpdateDevJob(project);
} else {
job = new UpdateJob(project);
}
job.setUser(false);
job.schedule();
newDepSinceLastSave = false;
newDevDepSinceLastSave = false;
}
if (buildpath && !update) {
buildPathManager.update();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean hasDevDepsInstalled() {
// is there like a more performant way to keep this list?
// keep in mind a composer update command can be run from outside eclipse
if (composerPackage.getRequireDev().size() > 0) {
VersionedPackage dev = composerPackage.getRequireDev().toArray()[0];
ComposerPackages installed = composerProject.getInstalledPackages();
return installed.has(dev.getName());
}
return false;
}
public void doSaveAs() {
}
public boolean isSaveAsAllowed() {
return false;
}
private void documentAboutToBeChanged(DocumentEvent event) {
}
private void documentChanged(DocumentEvent event) {
String contents = event.getDocument().get();
// changes happen outside eclipse
if (!pageChanging && !saving) {
if (isJsonEditor()) {
IDocument document = documentProvider.getDocument(getEditorInput());
if (document.get() != null && !document.get().equals(contents)) {
document.set(contents);
}
} else {
parse(contents);
}
setDirty(false);
}
// changes in eclipse
if (!saving && jsonDump != null && !jsonDump.equals(contents)) {
setDirty(true);
}
}
private boolean isJsonEditor() {
return getActiveEditor() == jsonEditor;
}
private void parse(String contents) {
try {
composerPackage.fromJson(contents);
setValidJson(true);
} catch (ParseException e) {
setValidJson(false, e);
}
}
private void validateJson(String contents) {
try {
new ComposerPackage(contents);
setValidJson(true);
} catch (ParseException e) {
setValidJson(false, e);
}
}
private void setValidJson(boolean valid) {
setValidJson(valid, null);
}
private void setValidJson(boolean valid, ParseException e) {
validJson = valid;
if (valid) {
removeMessage(MSG_PARSE_ERROR);
} else {
addMessage(MSG_PARSE_ERROR, "Invalid Json: " + e.getMessage(), IMessage.ERROR);
}
// change enabled status for pages
if (pages != null) {
for (Object pageObj : pages) {
if (pageObj instanceof ComposerFormPage && pageObj != jsonEditor) {
ComposerFormPage page = (ComposerFormPage) pageObj;
page.setEnabled(valid);
}
}
}
}
public boolean isValidJson() {
return validJson;
}
private void addMessage(String id, String message, int type) {
addMessage(id, message, type, null);
}
private void addMessage(String id, String message, int type, Object data) {
IManagedForm headerForm = getHeaderForm();
if (headerForm != null) {
headerForm.getMessageManager().addMessage(id, message, data, type);
}
}
private void removeMessage(String id) {
IManagedForm headerForm = getHeaderForm();
if (headerForm != null) {
headerForm.getMessageManager().removeMessage(id);
}
}
public boolean isDirty() {
return this.dirty;
}
public void setDirty(boolean value) {
this.dirty = value;
editorDirtyStateChanged();
}
public IProject getProject() {
return project;
}
public ComposerPackage getComposerPackge() {
return composerPackage;
}
/**
* Based on org.eclipse.m2e.editor.pom.MavenPomEditor
*/
private void resourceChanged(IResourceChangeEvent event) {
if (jsonFile == null) {
return;
}
// handle project delete
if (event.getType() == IResourceChangeEvent.PRE_CLOSE || event.getType() == IResourceChangeEvent.PRE_DELETE) {
if (jsonFile.getProject().equals(event.getResource())) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
close(false);
}
});
}
return;
}
// handle json delete
class RemovedResourceDeltaVisitor implements IResourceDeltaVisitor {
boolean removed = false;
public boolean visit(IResourceDelta delta) throws CoreException {
if (delta.getResource() != null && delta.getResource().equals(jsonFile) && (delta.getKind() & (IResourceDelta.REMOVED)) != 0) {
removed = true;
return false;
}
return true;
}
};
try {
RemovedResourceDeltaVisitor visitor = new RemovedResourceDeltaVisitor();
event.getDelta().accept(visitor);
if (visitor.removed) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
close(true);
}
});
}
} catch (CoreException ex) {
Logger.logException(ex);
}
}
}