| /******************************************************************************* |
| * Copyright (c) 2001, 2008 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.wst.xml.core.internal.emf2xml; |
| |
| import java.util.Collection; |
| import java.util.Iterator; |
| |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapter; |
| import org.eclipse.wst.common.internal.emf.resource.EMF2DOMAdapterImpl; |
| import org.eclipse.wst.common.internal.emf.resource.EMF2DOMRenderer; |
| import org.eclipse.wst.common.internal.emf.resource.Translator; |
| import org.eclipse.wst.common.internal.emf.resource.TranslatorResource; |
| import org.eclipse.wst.common.internal.emf.utilities.Assert; |
| import org.eclipse.wst.common.internal.emf.utilities.DOMUtilities; |
| import org.eclipse.wst.common.internal.emf.utilities.FeatureValueConversionException; |
| import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter; |
| import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| import org.eclipse.wst.xml.core.internal.Logger; |
| 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.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.Text; |
| |
| public class EMF2DOMSSEAdapter extends EMF2DOMAdapterImpl implements INodeAdapter { |
| |
| private Class resourceClass; |
| public EMF2DOMSSEAdapter(Node node, EMF2DOMRenderer renderer, Translator translator) { |
| super(node, renderer, translator); |
| } |
| |
| public EMF2DOMSSEAdapter(Notifier object, Node node, EMF2DOMRenderer renderer, Translator translator) { |
| super(object, node, renderer, translator); |
| } |
| |
| public EMF2DOMSSEAdapter(TranslatorResource resource, Document document, EMF2DOMRenderer renderer, Translator translator) { |
| super(resource, document, renderer, translator); |
| } |
| |
| protected String calcIndentString(Node node) { |
| Assert.isNotNull(node); |
| Assert.isNotNull(node.getParentNode(), "Node must be connected into the tree"); //$NON-NLS-1$ |
| |
| Node parent = node.getParentNode(); |
| |
| String indentString = getNewlineString(node); |
| |
| // Find indentation string for this node based on its sibling or |
| // parent |
| Node previousSibling = DOMUtilities.getPreviousNodeSibling(node); |
| if (previousSibling != null) { |
| indentString = primGetIndentString(previousSibling); |
| } |
| else { |
| String parentIndentString = primGetIndentString(parent); |
| indentString = parentIndentString + DOMUtilities.INDENT_STRING; |
| } |
| return indentString; |
| } |
| |
| /* |
| * Prints out a DOM notification for debugging. |
| */ |
| protected void debugDOMNotify(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue) { |
| if (fDebug) { |
| String notifType = ""; //$NON-NLS-1$ |
| switch (eventType) { |
| case INodeNotifier.ADD : |
| notifType = "ADD"; //$NON-NLS-1$ |
| break; |
| case INodeNotifier.REMOVE : |
| notifType = "REMOVE"; //$NON-NLS-1$ |
| break; |
| case INodeNotifier.CHANGE : |
| notifType = "CHANGE"; //$NON-NLS-1$ |
| break; |
| case INodeNotifier.CONTENT_CHANGED : |
| notifType = "CONTENT_CHANGED"; //$NON-NLS-1$ |
| break; |
| case INodeNotifier.STRUCTURE_CHANGED : |
| notifType = "STRUCTURE_CHANGE"; //$NON-NLS-1$ |
| break; |
| } |
| Logger.log(Logger.INFO_DEBUG, "DOM Change: " + notifType); //$NON-NLS-1$ |
| Logger.log(Logger.INFO_DEBUG, "\tnotifier : " + notifier); //$NON-NLS-1$ |
| Logger.log(Logger.INFO_DEBUG, "\tchangedFeature: " + changedFeature); //$NON-NLS-1$ |
| Logger.log(Logger.INFO_DEBUG, "\toldValue : " + oldValue); //$NON-NLS-1$ |
| Logger.log(Logger.INFO_DEBUG, "\tnewValue : " + newValue); //$NON-NLS-1$ |
| } |
| } |
| |
| protected void disableUndoManagementIfNecessary() { |
| IDOMModel model = getXMLModel(); |
| if (model != null && model.getUndoManager() != null) |
| model.disableUndoManagement(); |
| } |
| |
| protected void enableUndoManagement() { |
| IDOMModel model = getXMLModel(); |
| if (model != null && model.getUndoManager() != null) |
| model.enableUndoManagement(); |
| } |
| |
| protected String getNewlineString(Node node) { |
| /* |
| * We should always have IDOMNode, and IStructuredDocument, and |
| * consquently a valid "preferred" line delimiter, but just to be |
| * safe, we'll assign something by default. |
| */ |
| if (node instanceof IDOMNode) { |
| IDOMNode xmlNode = (IDOMNode) node; |
| IStructuredDocument document = xmlNode.getStructuredDocument(); |
| if (document != null) { |
| return document.getLineDelimiter(); |
| } |
| } |
| return DOMUtilities.NEWLINE_STRING; |
| } |
| |
| protected IDOMModel getXMLModel() { |
| if (getNode() != null) |
| return ((IDOMNode) getNode()).getModel(); |
| return null; |
| } |
| |
| protected IDOMNode getXMLNode() { |
| return (IDOMNode) getNode(); |
| } |
| |
| /* |
| * Do nothing for SSE, we will tolerate anything they add |
| */ |
| protected void handleFeatureValueConversionException(FeatureValueConversionException ex) { |
| // Do nothing |
| } |
| |
| /* |
| * Do nothing for SSE, we will tolerate anything they add |
| */ |
| protected void handleInvalidMultiNodes(String nodeName) { |
| // Do nothing |
| } |
| |
| protected void indent(Node node, Translator map) { |
| Assert.isNotNull(node.getParentNode(), "Node must be connected into the tree"); //$NON-NLS-1$ |
| Assert.isNotNull(node); |
| |
| String indentString = calcIndentString(node); |
| |
| // Indent before the start tag |
| indentStartTag(indentString, node, map); |
| |
| // Indent before the end tag |
| indentEndTag(indentString, node, map); |
| } |
| |
| /** |
| * Indent before the end tag of the <node>passed in. |
| */ |
| protected void indentEndTag(String indentString, Node node, Translator map) { |
| if (!map.shouldIndentEndTag(node)) |
| return; |
| String domPath = map.getDOMPath(); |
| |
| if ((!map.isManagedByParent() && !map.isDOMTextValue()) || (map.isManagedByParent() && domPath.length() != 0) && node.getNodeName().equals(domPath)) { |
| Text newWS = node.getOwnerDocument().createTextNode(getNewlineString(node) + indentString); //$NON-NLS-1$ |
| DOMUtilities.insertBeforeNode(node, newWS, null); |
| } |
| } |
| |
| /** |
| * Indent before the start tag of the <node>passed in. |
| */ |
| protected void indentStartTag(String indentString, Node node, Translator map) { |
| Node parent = node.getParentNode(); |
| Text newWS = node.getOwnerDocument().createTextNode(getNewlineString(node) + indentString); //$NON-NLS-1$ |
| DOMUtilities.insertAfterNode(parent, newWS, DOMUtilities.getPreviousNodeSibling(node)); |
| } |
| |
| protected boolean isEmptyTag(Element parent) { |
| return ((IDOMElement) parent).isEmptyTag(); |
| } |
| |
| /* |
| * This method is called when the DOM node changes. It attempts to update |
| * MOF object based on the changes. |
| */ |
| public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { |
| |
| if (!isNotificationEnabled()) |
| return; |
| |
| debugDOMNotify(notifier, eventType, changedFeature, oldValue, newValue); |
| |
| if (notifier != getNode() && eventType != INodeNotifier.CHANGE) { |
| // This is the case where the notification was sent from a |
| // sub node. Use the notifiers name to determine which |
| // MOF feature to update. Note that is is assumed that if |
| // the eventType is CHANGE then it attribute on a path node |
| // changing. This was put in for the EGL group. |
| if (notifier instanceof Element) { |
| if (eventType == INodeNotifier.STRUCTURE_CHANGED || eventType == INodeNotifier.CONTENT_CHANGED || eventType == INodeNotifier.CHANGE) { |
| Element notifyingNode = (Element) notifier; |
| Translator map = findTranslator(notifyingNode.getNodeName(), false); |
| if (map != null) |
| updateMOFFeature(map, getNode(), getEObject()); |
| } |
| } |
| } |
| else { |
| // Update everything on STRUCTURE_CHANGE or CONTENT_CHANGE. |
| // Other event types occur too often. |
| if (eventType == INodeNotifier.STRUCTURE_CHANGED || eventType == INodeNotifier.CONTENT_CHANGED) { |
| updateMOF(); |
| } |
| // Update just the attribute that changed. |
| else if (eventType == INodeNotifier.CHANGE) { |
| Translator map = findTranslator(changedFeature.toString(), true); |
| if (map != null) |
| updateMOFFeature(map, getNode(), getEObject()); |
| } |
| } |
| } |
| |
| protected void postUpdateDOMFeature(Translator map, Node node, EObject mofObject) { |
| enableUndoManagement(); |
| } |
| |
| protected void preUpdateDOMFeature(Translator map, Node node, EObject mofObject) { |
| super.preUpdateDOMFeature(map, node, mofObject); |
| disableUndoManagementIfNecessary(); |
| } |
| |
| protected void primAddDOMAdapter(Node aNode, EMF2DOMAdapter anAdapter) { |
| ((IDOMNode) aNode).addAdapter((EMF2DOMSSEAdapter) anAdapter); |
| } |
| |
| /** |
| * Create an adapter for a child DOM node |
| * |
| * @param node |
| * org.w3c.dom.Node The node to create the adapter for. |
| */ |
| protected EMF2DOMAdapter primCreateAdapter(EObject mofObject, Translator childMap) { |
| Element newNode = createNewNode(mofObject, childMap); |
| return new EMF2DOMSSEAdapter(mofObject, newNode, fRenderer, childMap); |
| } |
| |
| /** |
| * Create an adapter for a child DOM node |
| * |
| * @param node |
| * org.w3c.dom.Node The node to create the adapter for. |
| */ |
| protected EMF2DOMAdapter primCreateAdapter(Node node, Translator childMap) { |
| return new EMF2DOMSSEAdapter(node, fRenderer, childMap); |
| } |
| protected EMF2DOMAdapter createAdapter(Node node, Translator childMap) { |
| |
| //Assert.isNotNull(childMap.getChildAdapterClass()); |
| Assert.isNotNull(node); |
| |
| EMF2DOMAdapter adapter = primGetExistingAdapter(node); |
| |
| if (adapter != null) { |
| if (adapter.isMOFProxy() || adapter.getTarget() == null) { |
| removeDOMAdapter(node, adapter); |
| if (adapter.getTarget() != null) { |
| adapter.getTarget().eAdapters().remove(adapter); |
| } |
| adapter = null; |
| } |
| } else { |
| adapter = primCreateAdapter(node, childMap); |
| } |
| return adapter; |
| } |
| protected void addDOMAdapter(Node childNode) { |
| |
| // Only add the adapter if this is an child node that will not be |
| // adapted. For instance a subtree that maps to a MOF attribute |
| // setting. |
| if (childNode.getNodeType() == Node.ELEMENT_NODE) { |
| EMF2DOMAdapter attrAdapter = primGetExistingAdapter(childNode); |
| |
| if (attrAdapter == null || attrAdapter.getNode() != getNode()) { |
| // If the node is adapted, but not by this adapter then remove |
| // it. This happens to non-object children when the parent tag |
| // name is changed. |
| removeDOMAdapter(childNode, attrAdapter); |
| |
| if (fDebug) { |
| org.eclipse.jem.util.logger.proxy.Logger.getLogger().logError("\tCHILD: Adding DOM adapter: " + this); //$NON-NLS-1$ |
| org.eclipse.jem.util.logger.proxy.Logger.getLogger().logError("\t\tto: " + childNode); //$NON-NLS-1$ |
| } |
| primAddDOMAdapter(childNode, this); |
| } |
| } |
| } |
| |
| protected EMF2DOMAdapter primGetExistingAdapter(Node aNode) { |
| INodeNotifier sseNode = (INodeNotifier) aNode; |
| Collection adapters = sseNode.getAdapters(); |
| for (Iterator iterator = adapters.iterator(); iterator.hasNext();) { |
| INodeAdapter adapter = (INodeAdapter) iterator.next(); |
| // First Check if it's an EMF2DOMAdapter |
| if (adapter != null && adapter.isAdapterForType(EMF2DOMAdapter.ADAPTER_CLASS)) { |
| // Cast to EMF2DOMAdapter |
| EMF2DOMSSEAdapter e2DAdapter = (EMF2DOMSSEAdapter) adapter; |
| Object adapterTarget = e2DAdapter.getTarget(); |
| |
| //Handle the cases where either adapter's target is null |
| if ((getTarget() == null) || (adapterTarget == null)) |
| if (resourceClass != null && resourceClass.equals(e2DAdapter.getResourceClass())) |
| return e2DAdapter; |
| else |
| continue; |
| |
| |
| // Check if target is an EMF resource |
| if (getTarget() instanceof Resource) { |
| /* |
| * Now check if it's the right one (Multiple resources |
| * could be attached) |
| */ |
| if (adapterTarget != null && adapterTarget == getTarget()) { |
| return e2DAdapter; |
| } |
| } |
| else { |
| // Check if targets are EObjects with the same resources |
| EObject myTarget = (EObject) getTarget(); |
| /* |
| * Now check if it's the right one (Multiple resources could |
| * be attached) |
| */ |
| if (adapterTarget != null && ((EObject)adapterTarget).eResource() == myTarget.eResource()) { |
| return e2DAdapter; |
| } |
| } |
| |
| if (adapterTarget instanceof EObject) { |
| if (((EObject) e2DAdapter.getTarget()).eResource() == null) { |
| return e2DAdapter; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| protected String primGetIndentString(Node node) { |
| IStructuredDocument flatModel = ((IDOMNode) node).getStructuredDocument(); |
| int nodeStartOff = ((IDOMNode) node).getStartOffset(); |
| |
| int startOff = Math.max(0, nodeStartOff - 100); |
| int endOff = nodeStartOff; |
| |
| try { |
| String text = flatModel.get(startOff, endOff - startOff); |
| |
| int inx = text.length() - 1; |
| if (inx >= 0) { |
| for (; inx >= 0; inx--) { |
| char ch = text.charAt(inx); |
| if (Character.isWhitespace(ch) && ch != '\n' && ch != '\r') { |
| continue; |
| } |
| inx++; |
| break; |
| } |
| |
| return text.substring(inx); |
| } |
| } |
| catch (BadLocationException ex) { |
| Logger.logException(ex); |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| |
| protected void removeDOMAdapter(Node aNode, EMF2DOMAdapter anAdapter) { |
| ((IDOMNode) aNode).removeAdapter((INodeAdapter) anAdapter); |
| } |
| |
| |
| protected void reorderDOMChild(Node parentNode, Node childNode, Node insertBeforeNode, Translator map) { |
| super.reorderDOMChild(parentNode, childNode, insertBeforeNode, map); |
| // Since reordering deletes all the whitespace before the node, we |
| // must indent . |
| if (insertBeforeNode != null && insertBeforeNode.getNodeType() == Node.ELEMENT_NODE) |
| indentStartTag(calcIndentString(insertBeforeNode), insertBeforeNode, map); |
| else |
| indentStartTag(calcIndentString(childNode), childNode, map); |
| } |
| |
| protected void setEmptyTag(Element element) { |
| ((IDOMElement) element).setEmptyTag(true); |
| } |
| |
| public void updateDOM() { |
| if (!isNotificationEnabled()) |
| return; |
| try { |
| disableUndoManagementIfNecessary(); |
| primUpdateDOM(); |
| } |
| finally { |
| enableUndoManagement(); |
| } |
| } |
| |
| public Class getResourceClass() { |
| return resourceClass; |
| } |
| |
| protected void initChildTranslators() { |
| if (fRenderer != null && fRenderer.getResource() != null) |
| resourceClass = fRenderer.getResource().getClass(); |
| super.initChildTranslators(); |
| } |
| |
| } |