/******************************************************************************* * Copyright (c) 2008-2010 Sonatype, Inc. * 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: * Sonatype, Inc. - initial API and implementation *******************************************************************************/ package org.eclipse.m2e.model.edit.pom.translators; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.impl.ResourceImpl; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IModelManager; import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter; import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.eclipse.m2e.model.edit.pom.Model; import org.eclipse.m2e.model.edit.pom.PomFactory; public class SSESyncResource extends ResourceImpl { private static final Set<String> NO_EVENT_MODELS = new HashSet<String>(); private IDOMModel domModel; private Model pomModel; private Document doc; public SSESyncResource() { super(); } public SSESyncResource(URI uri) { super(uri); } @Override public void load(Map<?, ?> options) throws IOException { if(isLoaded()) { return; } loadDOMModel(); setProcessEvents(false); try { pomModel = PomFactory.eINSTANCE.createModel(); doc = domModel.getDocument(); DocumentAdapter da = new DocumentAdapter(); if(doc.getDocumentElement() != null) { createAdapterForRootNode(domModel.getDocument().getDocumentElement()).load(); } else { pomModel.eAdapters().add(da); } ((IDOMNode) doc).addAdapter(da); this.getContents().add(pomModel); this.setLoaded(true); } finally { setProcessEvents(true); } } @Override protected void doSave(OutputStream outputStream, Map<?, ?> options) throws IOException { try { domModel.save(outputStream); } catch(CoreException e) { IOException ioe = new IOException(e.getMessage()); ioe.initCause(e); throw ioe; } } @Override protected void doUnload() { domModel.releaseFromEdit(); } private void loadDOMModel() throws IOException { IFile ifile = null; if(uri.isPlatformResource()) { String localPath = uri.toPlatformString(true); ifile = (IFile) ResourcesPlugin.getWorkspace().getRoot().findMember(localPath); } else if(uri.isFile()) { String filePath = uri.toFileString(); File f = new File(filePath); IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(f.getAbsoluteFile().toURI()); if(files.length > 0) { ifile = files[0]; } } try { IModelManager modelManager = StructuredModelManager.getModelManager(); if(ifile != null && ifile.exists()) { domModel = (IDOMModel) modelManager.getExistingModelForEdit(ifile); } if(null == domModel) { if(ifile != null && ifile.exists()) { domModel = (IDOMModel) modelManager.getModelForEdit(ifile); } else if(uri.isFile()) { File f = new File(uri.toFileString()); FileInputStream is = new FileInputStream(f); try { domModel = (IDOMModel) modelManager.getModelForEdit(f.getAbsolutePath(), is, null); } finally { is.close(); } } // Had to comment this out, ExtensibleURIConverterImpl isn't available in Eclipse 3.3 // else { // ExtensibleURIConverterImpl converter = new ExtensibleURIConverterImpl(); // InputStream is = converter.createInputStream(uri); // domModel = (IDOMModel) modelManager.getModelForEdit(uri.toString(), is, null); // is.close(); // } } } catch(CoreException e) { // IOException can't wrap another exception before Java 6 if(e.getCause() != null && e.getCause() instanceof IOException) { throw (IOException) e.getCause(); } else { throw new IOException(e.getMessage()); } } } private ModelObjectAdapter createAdapterForRootNode(Element root) { ModelObjectAdapter adapter = new ModelObjectAdapter(this, pomModel, root); ((IDOMElement) root).addAdapter(adapter); pomModel.eAdapters().add(adapter); return adapter; } boolean isProcessEvents() { return !NO_EVENT_MODELS.contains(domModel.getId()); } void setProcessEvents(boolean processEvents) { if(processEvents) { NO_EVENT_MODELS.remove(domModel.getId()); } else { NO_EVENT_MODELS.add(domModel.getId()); } } private class DocumentAdapter implements INodeAdapter, Adapter { private Notifier target; public boolean isAdapterForType(Object type) { return getClass().equals(type); } public void notifyChanged(Notification notification) { if(isProcessEvents()) { setProcessEvents(false); try { int type = notification.getEventType(); if(Notification.ADD == type || Notification.ADD_MANY == type || Notification.SET == type) { if(null == doc.getDocumentElement()) { Element newRoot = doc.createElementNS("http://maven.apache.org/POM/4.0.0", //$NON-NLS-1$ "project"); //$NON-NLS-1$ newRoot.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", //$NON-NLS-1$ "xsi:schemaLocation", //$NON-NLS-1$ "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"); //$NON-NLS-1$ // I think this is just wrong...but can't find a // better way. newRoot.setAttribute("xmlns", //$NON-NLS-1$ "http://maven.apache.org/POM/4.0.0"); //$NON-NLS-1$ newRoot.setAttribute("xmlns:xsi", //$NON-NLS-1$ "http://www.w3.org/2001/XMLSchema-instance"); //$NON-NLS-1$ doc.appendChild(newRoot); pomModel.setModelVersion("4.0.0"); //$NON-NLS-1$ createAdapterForRootNode(newRoot).save(); } else { Element root = doc.getDocumentElement(); createAdapterForRootNode(root).load(); } DocumentAdapter existingDocAdapter = (DocumentAdapter) EcoreUtil.getExistingAdapter(pomModel, DocumentAdapter.class); if(null != existingDocAdapter) { pomModel.eAdapters().remove(existingDocAdapter); } } } finally { setProcessEvents(true); } } } public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { if(isProcessEvents()) { setProcessEvents(false); try { if(INodeNotifier.ADD == eventType) { if(newValue instanceof Element) { Element e = (Element) newValue; if(doc.getDocumentElement().equals(e)) { DocumentAdapter existingDocAdapter = (DocumentAdapter) EcoreUtil.getExistingAdapter(pomModel, DocumentAdapter.class); if(null != existingDocAdapter) { pomModel.eAdapters().remove(existingDocAdapter); } createAdapterForRootNode(e).load(); } } } else if(INodeNotifier.REMOVE == eventType) { if(changedFeature instanceof Element) { ModelObjectAdapter existing = (ModelObjectAdapter) EcoreUtil.getExistingAdapter(pomModel, ModelObjectAdapter.class); if(existing != null) { pomModel.eAdapters().remove(existing); } if(null == doc.getDocumentElement()) { for(EStructuralFeature feature : pomModel.eClass().getEStructuralFeatures()) { pomModel.eUnset(feature); } } DocumentAdapter existingDocAdapter = (DocumentAdapter) EcoreUtil.getExistingAdapter(pomModel, DocumentAdapter.class); if(null == existingDocAdapter) { pomModel.eAdapters().add(this); } } } } finally { setProcessEvents(true); } } } public Notifier getTarget() { return target; } public void setTarget(Notifier newTarget) { this.target = newTarget; } } }