/*******************************************************************************
* Copyright (c) 2004, 2008 John Krasnay and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* John Krasnay - initial API and implementation
*******************************************************************************/
package net.sf.vex.editor.config;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import net.sf.vex.dom.DocumentWriter;
import net.sf.vex.editor.VexPlugin;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* Represents a Vex plugin project.
*/
public class PluginProject extends ConfigSource {
public static final String PLUGIN_XML = "vex-plugin.xml"; //$NON-NLS-1$
public static final String PROJECT_CONFIG_SER = ".vexConfig.ser"; //$NON-NLS-1$
/**
* Class constructor.
* @param config VexConfiguration associated with this project.
*/
protected PluginProject(IProject project) {
this.projectPath = project.getFullPath().toString();
}
/**
* Remove the .vexConfig.ser state in which the project state is stored.
*/
public void cleanState() throws CoreException {
IFile configSer = this.getProject().getFile(PROJECT_CONFIG_SER);
configSer.delete(true, null);
}
/**
* Factory method that returns the plugin project for the given
* IProject. If the given project does not have the Vex plugin
* project nature, null is returned. PluginProject instances are
* cached so they can be efficiently returned.
*
* @param project IProject for which to return the PluginProject.
* @return
* @throws CoreException
*/
public static PluginProject get(IProject project) {
List sources = ConfigRegistry.getInstance().getAllConfigSources();
for (Iterator it = sources.iterator(); it.hasNext();) {
ConfigSource source = (ConfigSource) it.next();
if (source instanceof PluginProject) {
if (project.equals(((PluginProject) source).getProject())) {
return (PluginProject) source;
}
}
}
return null;
}
public URL getBaseUrl() {
try {
return this.getProject().getLocation().toFile().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(Messages.getString("PluginProject.malformedUrl"), e); //$NON-NLS-1$
}
}
/**
* Returns the IProject associated with this plugin project.
*/
public IProject getProject() {
return ResourcesPlugin.getWorkspace().getRoot().getProject(this.projectPath);
}
/**
* Loads the project from it's serialized state file and registers it with
* the ConfigRegistry. If the serialized state cannot be loaded, a new
* PluginProject is created and the builder is launched.
*/
public static PluginProject load(IProject project) {
try {
if (!project.isOpen() || !project.hasNature(PluginProjectNature.ID)) {
String message = MessageFormat.format(
Messages.getString("PluginProject.notPluginProject"), //$NON-NLS-1$
new Object[] { project.getName() });
throw new IllegalArgumentException(message);
}
} catch (CoreException e) {
String message = MessageFormat.format(
Messages.getString("PluginProject.notPluginProject"), //$NON-NLS-1$
new Object[] { project.getName() });
throw new IllegalArgumentException(message);
}
IFile serFile = project.getFile(PROJECT_CONFIG_SER);
PluginProject pluginProject = null;
if (serFile.exists()) {
try {
ObjectInputStream ois = new ObjectInputStream(serFile.getContents());
pluginProject = (PluginProject) ois.readObject();
} catch (Exception ex) {
String message = MessageFormat.format(
Messages.getString("PluginProject.loadingError"), //$NON-NLS-1$
new Object[] { serFile });
VexPlugin.getInstance().log(IStatus.WARNING, message, ex);
}
}
boolean rebuild = false;
if (pluginProject == null) {
rebuild = true;
pluginProject = new PluginProject(project);
}
ConfigRegistry registry = ConfigRegistry.getInstance();
registry.addConfigSource(pluginProject);
registry.fireConfigChanged(new ConfigEvent(PluginProject.class));
if (rebuild) {
try {
project.build(IncrementalProjectBuilder.FULL_BUILD, null);
} catch (Exception ex) {
String message = MessageFormat.format(
Messages.getString("PluginProject.buildError"), //$NON-NLS-1$
new Object[] { project.getName() });
VexPlugin.getInstance().log(IStatus.ERROR, message, ex);
}
}
return pluginProject;
}
/**
* Re-parses the vex-plugin.xml file.
*/
public void parseConfigXml() throws SAXException, IOException {
DocumentBuilder builder;
try {
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (FactoryConfigurationError e) {
throw new RuntimeException(e);
}
this.removeAllItems();
URL url = new URL(this.getBaseUrl(), PluginProject.PLUGIN_XML);
Document doc = builder.parse(url.toString());
Element root = doc.getDocumentElement();
this.setUniqueIdentifer(root.getAttribute("id")); //$NON-NLS-1$
NodeList nodeList = doc.getElementsByTagName("extension"); //$NON-NLS-1$
for (int i = 0; i < nodeList.getLength(); i++) {
Element element = (Element) nodeList.item(i);
String extPoint = element.getAttribute("point"); //$NON-NLS-1$
String id = element.getAttribute("id"); //$NON-NLS-1$
String name = element.getAttribute("name"); //$NON-NLS-1$
List configElementList = new ArrayList();
NodeList childList = element.getChildNodes();
for (int j = 0; j < childList.getLength(); j++) {
Node child = childList.item(j);
if (child instanceof Element) {
configElementList.add(child);
}
}
IConfigElement[] configElements = new IConfigElement[configElementList.size()];
for (int j = 0; j < configElementList.size(); j++) {
configElements[j] = new DomConfigurationElement((Element) configElementList.get(j));
}
this.addItem(extPoint, id, name, configElements);
}
}
/**
* Saves the state of this project into .vexConfig.ser.
*/
public void saveState() throws CoreException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
IFile configSer = this.getProject().getFile(PROJECT_CONFIG_SER);
if (configSer.exists()) {
configSer.setContents(bais, true, false, null);
} else {
configSer.create(bais, true, null);
configSer.setDerived(true);
}
}
/**
* Writes this configuraton to the file vex-config.xml in the project.
*/
public void writeConfigXml() throws CoreException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter out = new PrintWriter(new OutputStreamWriter(baos, "utf-8")); //$NON-NLS-1$
ConfigurationElement root = new ConfigurationElement("plugin"); //$NON-NLS-1$
for (Iterator it = this.getAllItems().iterator(); it.hasNext();) {
ConfigItem item = (ConfigItem) it.next();
ConfigurationElement extElement = new ConfigurationElement("extension"); //$NON-NLS-1$
extElement.setAttribute("id", item.getSimpleId()); //$NON-NLS-1$
extElement.setAttribute("name", item.getName()); //$NON-NLS-1$
extElement.setAttribute("point", item.getExtensionPointId()); //$NON-NLS-1$
IConfigItemFactory factory = ConfigRegistry.getInstance().getConfigItemFactory(item.getExtensionPointId());
extElement.setChildren(factory.createConfigurationElements(item));
root.addChild(extElement);
}
writeElement(root, out, 0);
out.close();
InputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
IFile file = this.getProject().getFile(PLUGIN_XML);
if (file.exists()) {
file.setContents(inputStream, true, false, null);
} else {
file.create(inputStream, true, null);
}
}
//=========================================================== PRIVATE
private String projectPath;
/** Filename used when serializing in a Vex plugin project */
public static final String SER_FILE = ".vexConfig.ser"; //$NON-NLS-1$
private static void writeElement(IConfigElement element, PrintWriter out, int level) {
StringBuffer elementIndent = new StringBuffer();
for (int i = 0; i < level; i++) {
elementIndent.append(" "); //$NON-NLS-1$
}
StringBuffer elementPrefix = new StringBuffer();
elementPrefix.append("<"); //$NON-NLS-1$
elementPrefix.append(element.getName());
StringBuffer attributeIndent = new StringBuffer(elementIndent.toString());
for (int i = 0; i < elementPrefix.length() + 1; i++) {
attributeIndent.append(" "); //$NON-NLS-1$
}
out.print(elementIndent.toString());
out.print(elementPrefix.toString());
String[] attributeNames = element.getAttributeNames();
for (int i = 0; i < attributeNames.length; i++) {
String attributeName = attributeNames[i];
if (i > 0) {
out.println();
out.print(attributeIndent);
} else {
out.print(" "); //$NON-NLS-1$
}
out.print(attributeName);
out.print("=\""); //$NON-NLS-1$
out.print(DocumentWriter.escape(element.getAttribute(attributeName)));
out.print("\""); //$NON-NLS-1$
}
out.println(">"); //$NON-NLS-1$
IConfigElement[] children = element.getChildren();
for (int i = 0; i < children.length; i++) {
writeElement(children[i], out, level+1);
}
out.print(elementIndent);
out.print("</"); //$NON-NLS-1$
out.print(element.getName());
out.println(">"); //$NON-NLS-1$
}
}