blob: 3fa734c73b3ea20edd6eaac2e11a38dc4c59b0bd [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2007 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.common.internal.emf.resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
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.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.wst.common.internal.emf.plugin.EcoreUtilitiesPlugin;
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.EtoolsCopySession;
import org.eclipse.wst.common.internal.emf.utilities.ExtendedEcoreUtil;
import org.eclipse.wst.common.internal.emf.utilities.FeatureValueConversionException;
import org.eclipse.wst.common.internal.emf.utilities.Revisit;
import org.eclipse.wst.common.internal.emf.utilities.StringUtil;
import org.eclipse.wst.common.internal.emf.utilities.TranslatorService;
import org.eclipse.wst.common.internal.emf.utilities.WFTUtilsResourceHandler;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
public class EMF2DOMAdapterImpl extends AdapterImpl implements EMF2DOMAdapter {
final protected static boolean fDebug = false;
protected boolean fNotificationEnabled = true;
protected Node fNode;
protected Translator fTranslator;
protected EMF2DOMRenderer fRenderer;
protected Translator[] childTranslators;
protected boolean isRoot = false;
private static final String PLATFORM = "org.eclipse.core.runtime.Platform"; //$NON-NLS-1$
private static final String ISRUNNING = "isRunning"; //$NON-NLS-1$
private class DependencyAdapter extends org.eclipse.emf.common.notify.impl.AdapterImpl {
static final String KEY = "EMF2DOMDependencyAdapter"; //$NON-NLS-1$
@Override
public void notifyChanged(Notification msg) {
EMF2DOMAdapterImpl.this.notifyChanged(msg);
}
/**
* @see org.eclipse.emf.common.notify.impl.AdapterImpl#isAdapterForType(Object)
*/
@Override
public boolean isAdapterForType(Object type) {
return KEY.equals(type);
}
}
/**
* Constructor for root adapters.
*/
public EMF2DOMAdapterImpl(TranslatorResource resource, Document document, EMF2DOMRenderer renderer, Translator translator) {
this((Notifier) resource, (Node) document, renderer, translator);
isRoot = true;
}
/**
* Construct an Adapter given an EObject and a node
*/
public EMF2DOMAdapterImpl(Notifier object, Node node, EMF2DOMRenderer renderer, Translator translator) {
super();
setTarget(object);
fNode = node;
fRenderer = renderer;
fTranslator = translator;
initChildTranslators();
addEMFAdapter();
addDOMAdapter();
addDependencyAdapterIfNecessary();
}
/**
* Construct an adapter from a DOM Node. The EObject will be created
*/
public EMF2DOMAdapterImpl(Node node, EMF2DOMRenderer renderer, Translator translator) {
fNode = node;
fRenderer = renderer;
fTranslator = translator;
setTargetFromNode();
initChildTranslators();
addEMFAdapter();
addDOMAdapter();
addDependencyAdapterIfNecessary();
}
protected void addDependencyAdapterIfNecessary() {
if (!fTranslator.isDependencyParent())
return;
EObject child = fTranslator.basicGetDependencyObject(getEObject());
if (child != null)
addDependencyAdapter(child);
}
protected void initChildTranslators() {
List children = new ArrayList();
boolean isRunning = false; //is the OSGI platform running ?
try {
// If the Platform class can be found, then continue to check if the OSGI platform is running
Class clazz = Class.forName(PLATFORM);
Method m = clazz.getMethod(ISRUNNING);
isRunning = ((Boolean)m.invoke(clazz)).booleanValue();
} catch (ClassNotFoundException e) {
// Ignore because this must be in a non_OSGI environment
} catch (SecurityException e) {
// Ignore because this must be in a non_OSGI environment
} catch (NoSuchMethodException e) {
// Ignore because this must be in a non_OSGI environment
} catch (IllegalArgumentException e) {
// Ignore because this must be in a non_OSGI environment
} catch (IllegalAccessException e) {
// Ignore because this must be in a non_OSGI environment
} catch (InvocationTargetException e) {
// Ignore because this must be in a non_OSGI environment
}
//Check for extended child translators because we are in OSGI mode
if (isRunning) {
Translator[] extendedChildren = TranslatorService.getInstance().getTranslators();
for (int i = 0; i < extendedChildren.length; i++) {
if (extendedChildren[i] != null)
children.add(extendedChildren[i]);
}
}
children.addAll(Arrays.asList(fTranslator.getChildren(getTarget(), fRenderer.getVersionID())));
VariableTranslatorFactory factory = fTranslator.getVariableTranslatorFactory();
if (factory != null) {
String domName = null;
NamedNodeMap map = fNode.getAttributes();
if (map != null) {
int length = map.getLength();
for (int i = 0; i < length; i++) {
Node attrNode = map.item(i);
domName = attrNode.getNodeName();
//Handle variable translators
Translator t = fTranslator.findChild(domName, getTarget(), fRenderer.getVersionID());
if (t != null && !children.contains(t))
children.add(t);
}
}
List childrenFromEMF = factory.create(getTarget());
if (childrenFromEMF != null)
children.addAll(childrenFromEMF);
}
childTranslators = (Translator[]) children.toArray(new Translator[children.size()]);
}
/**
* Set to false and notification of changes from both the DOM node and the MOF object will be
* ignored.
*/
public boolean isNotificationEnabled() {
return fNotificationEnabled;
}
/**
* Set to false and notification of changes from both the DOM node and the MOF object will be
* ignored.
*/
public void setNotificationEnabled(boolean isEnabled) {
fNotificationEnabled = isEnabled;
}
@Override
public boolean isAdapterForType(Object type) {
return EMF2DOMAdapter.ADAPTER_CLASS == type;
}
protected void addEMFAdapter() {
target.eAdapters().add(this);
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(shortClassName(this));
sb.append('(');
sb.append(getNode().getNodeName());
sb.append(',');
sb.append(shortClassName(getTarget()));
sb.append(')');
return sb.toString();
}
protected String shortClassName(Object o) {
if (o != null) {
String cn = o.getClass().getName();
int i = cn.lastIndexOf('.');
return cn.substring(i + 1, cn.length());
}
return null;
}
/*
* Prints out a MOF notification for debugging
*/
protected void debugMOFNotify(Notification msg) {
if (fDebug) {
String notifType = ""; //$NON-NLS-1$
switch (msg.getEventType()) {
case Notification.ADD :
notifType = "ADD"; //$NON-NLS-1$
break;
case Notification.REMOVE :
notifType = "REMOVE"; //$NON-NLS-1$
break;
case Notification.ADD_MANY :
notifType = "ADD_MANY"; //$NON-NLS-1$
break;
case Notification.REMOVE_MANY :
notifType = "REMOVE_MANY"; //$NON-NLS-1$
break;
case Notification.SET : {
if (msg.getPosition() == Notification.NO_INDEX)
notifType = "REPLACE"; //$NON-NLS-1$
else
notifType = "SET"; //$NON-NLS-1$
break;
}
case Notification.UNSET :
notifType = "UNSET"; //$NON-NLS-1$
break;
}
EcoreUtilitiesPlugin.logError("MOF Change: " + notifType); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tnotifier : " + msg.getNotifier()); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tchangedFeature: " + msg.getFeature()); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\toldValue : " + msg.getOldValue()); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tnewValue : " + msg.getNewValue()); //$NON-NLS-1$
}
}
/*
* Override this method to create the feature maps for the adapter.
*/
protected Translator[] getChildTranslators() {
return childTranslators;
}
protected Translator findTranslator(Notification not) {
if (not.getFeature() == null)
return null;
Translator[] maps = getChildTranslators();
for (int i = 0; i < maps.length; i++) {
if (maps[i].isMapFor(not.getFeature(), not.getOldValue(), not.getNewValue()))
return maps[i];
}
return null;
}
/**
* Checks to ensure that only one MOF node has been removed and will remove the dom equivalent.
* If multiple differences are found, will return the index of the first difference.
*
* @param parent
* @param mofChildren
* @param domChildren
* @return
*/
private int updateDOMSingleRemove(Node parent, List mofChildren, List domChildren){
int domIndex = 0;
int firstDifference = -1;
for (Object mofChild : mofChildren){
if (mofChild instanceof EObject)
{
EMF2DOMAdapter adapter = getExistingAdapter((EObject)mofChild);
if(adapter.getNode() != domChildren.get(domIndex)){
if(firstDifference != -1){
//second difference; return
return firstDifference;
}
//we found something that is different
firstDifference = domIndex;
//skip over one index and check against next
domIndex ++;
if(adapter.getNode() != domChildren.get(domIndex)){
//if these are different then there are multiple changes
return firstDifference;
}
}
domIndex++;
}
}
if(firstDifference != -1){
removeDOMChild(parent, (Element) domChildren.get(firstDifference));
//if we are here, and firstDifference is not -1, that means we have iterated
//through the entire mof list, and all elements have been processed. Return
//domchildren.size, so that no more processing is done in calling method.
return domChildren.size();
}
//if we are here, then firstDifference was -1. Our algo has failed. Let original algo run.
return 0;
}
/**
* Update all the children of the target MOF object in the relationship described by
*
* @map.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator Describes the mapping from the MOF
* attribute name to the DOM node name
*/
protected void primUpdateDOMMultiFeature(Translator map, Node node, List mofChildren, List domChildren, Notifier owner) {
int i = 0;
Node parent = findDOMPath(node, map, false);
if(domChildren.size() - mofChildren.size() == 1){
//an emf object has been deleted. Remove it from the dom as well
i = updateDOMSingleRemove(parent, mofChildren, domChildren);
}
//Used for inserting primitives
List inorderDOMChildren = null;
if(i < mofChildren.size()){
if (!map.isObjectMap() || map.isManagedByParent()) {
inorderDOMChildren = new ArrayList();
inorderDOMChildren.addAll(domChildren);
}
}
// Go though the MOF children checking to see if the corresponding
// MOF Adapter children exists. If not, create the adapter.
// Also handles reordering children that have moved.
for (; i < mofChildren.size(); i++) {
Object child = mofChildren.get(i);
EObject mofChild = null;
EMF2DOMAdapter adapter = null;
// Check to see if the child is a MOF Object.
if (!map.isManagedByParent() && child instanceof EObject) {
mofChild = (EObject) mofChildren.get(i);
adapter = getExistingAdapter(mofChild);
}
if (adapter != null && i < domChildren.size() && domChildren.get(i) == adapter.getNode())
continue;
if (adapter != null) {
if (domChildren.isEmpty())
continue;
// A node has been reordered in the list
Node reorderNode = adapter.getNode();
Node insertBeforeNode = reorderNode;
if (i < domChildren.size() && domChildren.get(i) != reorderNode) {
insertBeforeNode = (Node) domChildren.get(i);
}
domChildren.remove(reorderNode);
domChildren.add(i, reorderNode);
if (reorderNode != insertBeforeNode) {
reorderDOMChild(parent, reorderNode, insertBeforeNode, map);
}
} else {
// A new node has been added, create it
parent = createDOMPath(node, map);
if (mofChild != null) {
adapter = createAdapter(mofChild, map);
Node newNode = adapter.getNode();
Node insertBeforeNode = findInsertBeforeNode(parent, map, mofChildren, i, domChildren);
DOMUtilities.insertBeforeNodeAndWhitespace(parent, newNode, insertBeforeNode);
domChildren.add(i, newNode);
boolean notificationFlag = adapter.isNotificationEnabled();
adapter.setNotificationEnabled(false);
try {
indent(newNode, map);
} finally {
adapter.setNotificationEnabled(notificationFlag);
}
adapter.updateDOM();
} else {
// The mof feature is a collection of primitives.
// create a new dom node and listen to it.
Element newNode = createNewNode(null, map);
Node insertBeforeNode = findInsertBeforeNode(parent, map, mofChildren, i, inorderDOMChildren);
DOMUtilities.insertBeforeNodeAndWhitespace(parent, newNode, insertBeforeNode);
indent(newNode, map);
addDOMAdapter(newNode); // Hook up listeners
domChildren.add(i, newNode);
if(inorderDOMChildren != null){
inorderDOMChildren.add(newNode);
}
Text newText = parent.getOwnerDocument().createTextNode(map.convertValueToString(child, (EObject) owner));
DOMUtilities.insertBeforeNode(newNode, newText, null);
}
}
}
// Remove any remaining adapters.
for (; i < domChildren.size(); i++) {
removeDOMChild(parent, (Element) domChildren.get(i));
}
// If there are no MOF children, remove any unnecessary DOM node paths
if (mofChildren.size() == 0 && map.hasDOMPath()) {
if (map.shouldRenderEmptyDOMPath((EObject) owner))
createDOMPath(node, map);
else
removeDOMPath(node, map);
}
}
/**
* Update all the children of the target MOF object in the relationship described by
*
* @map.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator Describes the mapping from the MOF
* attribute name to the DOM node name
*/
protected void primUpdateMOFMultiFeature(Translator map, Node node, List mofChildren, List domChilren) {
Hashtable nodeToAdapter = new Hashtable();
for (int i = 0; i < mofChildren.size(); i++) {
EMF2DOMAdapter adapter = getExistingAdapter((EObject) mofChildren.get(i));
if (adapter != null)
nodeToAdapter.put(adapter.getNode(), adapter);
}
// Go though the DOM children checking to see if the corresponding
// MOF Adapter children exists. If not, create the adapter.
// Also handles reordering children that have moved.
int i = 0;
int mofIndex = 0;
List adaptersToUpdate = new ArrayList();
for (; i < domChilren.size(); i++) {
Element childNode = (Element) domChilren.get(i);
EMF2DOMAdapter adapter = i < mofChildren.size() ? getExistingAdapter((EObject) mofChildren.get(i)) : null;
if (adapter != null && !adapter.isMOFProxy() && adapter.getNode() == childNode) {
// Because the adapter is processing STRUCTURE_CHANGED from the
// DOM, we
// must update all the way down the tree since anything under
// the notifying
// DOM node could have changed.
adapter.updateMOF();
mofIndex++;
continue;
}
adapter = (EMF2DOMAdapter) nodeToAdapter.get(childNode);
if (adapter != null) {
reorderIfNecessary((EList) mofChildren, adapter.getEObject(), mofIndex);
mofIndex++;
} else {
adapter = createAdapter(childNode, map);
if (adapter != null) {
try {
//We don't want to push anything back to the child dom
adapter.setNotificationEnabled(false);
map.setMOFValue(getTarget(), adapter.getTarget(), mofIndex);
} finally {
adapter.setNotificationEnabled(true);
}
adaptersToUpdate.add(adapter);
mofIndex++;
}
}
}
// Remove any remaining adapters.
//make a copy so we remove all items - bug 192468
Object[] childrenArray = mofChildren.toArray();
for (; i < childrenArray.length; i++) {
removeMOFValue((EObject) childrenArray[i], map);
}
// The adapters cannot be updated as they created. We must wait until
// all of the adapters are created and removed before updating,
// otherwise
// we can get in a state where there are adapters fighting with
// eachother
// (one for the old node and one for the new node).
for (int j = 0; j < adaptersToUpdate.size(); j++) {
((EMF2DOMAdapter) adaptersToUpdate.get(j)).updateMOF();
}
}
/**
* Removes a feature's value.
*
* @param childAdapter
* com.ibm.etools.mof2dom.EMF2DOMAdapter The child to remove
* @param mofAttributeName
* String The name of the mofAttribute to remove the child from.
*/
protected void removeMOFValue(EObject value, Translator translator) {
if (value == null)
return;
EMF2DOMAdapter adapter = (EMF2DOMAdapter) EcoreUtil.getExistingAdapter(value, EMF2DOMAdapter.ADAPTER_CLASS);
if (adapter != null) {
// Remove the adapter from BOTH the MOF Object and the DOM Nodes
removeAdapters(adapter.getNode());
value.eAdapters().remove(adapter);
}
EStructuralFeature feature = translator.getFeature();
boolean doUnload = feature == null || (translator.isObjectMap() && ((EReference) feature).isContainment());
// translator.removeMOFValue() was here originally
// Unload the objects.
if (doUnload)
ExtendedEcoreUtil.unload(value);
// Remove the MOF value
translator.removeMOFValue(getTarget(), value);
}
protected void reorderIfNecessary(EList emfChildren, EObject eObj, int emfIndex) {
int currIndex = emfChildren.indexOf(eObj);
if (currIndex > -1 && currIndex != emfIndex)
emfChildren.move(emfIndex, eObj);
}
/**
* default is to do nothing; subclasses can override
*/
protected void indent(Node newNode, Translator map) {
}
protected Node findInsertBeforeNode(Node parentNode, Translator map, List mofList, int mofInx, List domList) {
Node insertBeforeNode = null;
// If there are no current dom children for this map, find the initial
// insert pos.
if (domList.size() == 0)
return findInitialInsertBeforeNode(parentNode, map);
// If some dom nodes then find the correct one to insert before.
int i = mofInx + 1;
while (i < mofList.size() && insertBeforeNode == null) {
// Start at the mofInx passed in and look forward for the first
// adapted
// MOF object. Use that node as the insert before node.
Object o = mofList.get(i);
if (!map.isObjectMap() || map.isManagedByParent())
break;
EObject tMOFObject = (EObject) o;
EMF2DOMAdapter tAdapter = (EMF2DOMAdapter) EcoreUtil.getExistingAdapter(tMOFObject, EMF2DOMAdapter.ADAPTER_CLASS);
if (tAdapter != null) {
insertBeforeNode = tAdapter.getNode();
}
i++;
}
// Handle inserting at the end of the list
if (insertBeforeNode == null)
insertBeforeNode = DOMUtilities.getNextNodeSibling((Node) domList.get(domList.size() - 1));
if (insertBeforeNode == null)
insertBeforeNode = ((Node) domList.get(domList.size() - 1)).getNextSibling();
return insertBeforeNode;
}
protected Node findInitialInsertBeforeNode(Node parentNode, Translator mapNode) {
Translator[] maps = getChildTranslators();
// First, skip past all the maps in the ordered collection
// of maps. We want to begin the search with this node.
int i = 0;
for (; i < maps.length; i++) {
if (maps[i] == mapNode)
break;
}
// Now search go through each map node until a child node matching
// its DOM name is found.
Node insertBeforeNode = null;
for (int j = i; j < maps.length && insertBeforeNode == null; j++) {
NodeList childNodes = parentNode.getChildNodes();
Translator nodeToFindMap = maps[j];
for (int k = 0; k < childNodes.getLength(); k++) {
Node node = childNodes.item(k);
if (nodeToFindMap.isMapFor(node.getNodeName())) {
insertBeforeNode = node;
break;
}
}
}
return insertBeforeNode;
}
/*
* Traverses the path that <map> specifies. Returns the last node of the path that was able to
* be traversed or null if the path could not be traversed. The <addAdapters> boolean is used to
* determine if the receiver is added as an adapter to every node found on the path.
*/
protected Node findDOMPath(Node parent, Translator map, boolean addAdapters) {
String path = map.getDOMPath();
Node curNode = parent;
Iterator iter = DOMUtilities.createPathIterator(path);
while (curNode != null && iter.hasNext()) {
String nodeName = (String) iter.next();
curNode = DOMUtilities.getNodeChild(curNode, nodeName);
if (addAdapters && curNode != null) {
addDOMAdapter(curNode);
}
}
return curNode;
}
/**
* Return the list of DOM node children that currently exist with the specified tagname.
*/
protected List getDOMChildren(Node node, Translator map) {
Node parent = findDOMPath(node, map, true);
if (parent != null)
return DOMUtilities.getNodeChildren(parent, map.getDOMNames());
return new ArrayList();
}
protected EMF2DOMAdapter getExistingAdapter(EObject refObject) {
EMF2DOMAdapter adapter = (EMF2DOMAdapter) EcoreUtil.getExistingAdapter(refObject, EMF2DOMAdapter.ADAPTER_CLASS);
if (adapter != null && adapter.isMOFProxy()) {
removeDOMAdapter(adapter.getNode(), adapter);
refObject.eAdapters().remove(adapter);
adapter = null;
}
return adapter;
}
protected void primAddDOMAdapter(Node aNode, EMF2DOMAdapter anAdapter) {
fRenderer.registerDOMAdapter(aNode, anAdapter);
}
protected EMF2DOMAdapter primGetExistingAdapter(Node aNode) {
return fRenderer.getExistingDOMAdapter(aNode);
}
protected void removeDOMAdapter(Node aNode, EMF2DOMAdapter anAdapter) {
fRenderer.removeDOMAdapter(aNode, anAdapter);
}
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) {
EcoreUtilitiesPlugin.logError("\tCHILD: Adding DOM adapter: " + this); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\t\tto: " + childNode); //$NON-NLS-1$
}
primAddDOMAdapter(childNode, this);
}
}
}
/**
* Reorder a child before a given node
*/
protected void reorderDOMChild(Node parentNode, Node childNode, Node insertBeforeNode, Translator map) {
try {
removeDOMChild(parentNode, childNode, false);
parentNode.insertBefore(childNode, insertBeforeNode);
} catch (Exception e) {
EcoreUtilitiesPlugin.logError(e);
}
}
protected String getNewlineString(Node node) {
return DOMUtilities.NEWLINE_STRING;
}
protected String primGetIndentString(Node node) {
return DOMUtilities.getIndentString(node);
}
/**
* Remove a child node
*/
protected void removeDOMChild(Node parentNode, Node childNode) {
removeDOMChild(parentNode, childNode, true);
}
/**
* Remove a child node
*/
protected void removeDOMChild(Node parentNode, Node childNode, boolean removeAdapter) {
try {
if (childNode == null)
return;
// Look for any whitespace preceeding the node being
// removed and remove it as well.
Text prevText = DOMUtilities.getPreviousTextSibling(childNode);
if (prevText != null && DOMUtilities.isWhitespace(prevText)) {
parentNode.removeChild(prevText);
}
// Remove the node.
if (removeAdapter)
removeAdapters(childNode);
parentNode.removeChild(childNode);
} catch (Exception e) {
EcoreUtilitiesPlugin.logError(e);
}
}
/**
* Remove the DOM adapters from the node AND all its child nodes, recursively.
*/
public void removeAdapters(Node node) {
EMF2DOMAdapter adapter = primGetExistingAdapter(node);
if (adapter != null) {
// Remove the adapter from both the DOM node and the MOF Object.
removeDOMAdapter(node, adapter);
if (adapter.getNode() == node) {
Notifier localTarget = adapter.getTarget();
if (localTarget != null)
localTarget.eAdapters().remove(adapter);
}
}
NodeList nl = node.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node n = nl.item(i);
removeAdapters(n);
}
}
/**
* Creates the path specified by <map>under <node>. Only the portion of the path that does not
* exist (if any) is created
*
* @param node
* org.w3c.dom.Node
* @param map
* com.ibm.etools.mof2dom.Translator
*/
protected Node createDOMPath(Node node, Translator map) {
Iterator i = DOMUtilities.createPathIterator(map.getDOMPath());
Node curNode = node;
while (i.hasNext()) {
String nodeName = (String) i.next();
curNode = findOrCreateNode(node, map, nodeName);
}
return curNode;
}
protected Element findOrCreateNode(Node parent, Translator map, String segment) {
Node node = DOMUtilities.getNodeChild(parent, segment);
if (node == null) {
// The node did not already exist, create it.
Document doc = parent.getOwnerDocument();
node = doc.createElement(segment);
if (map.isEmptyTag())
setEmptyTag((Element) node);
Node insertBeforeNode = findInitialInsertBeforeNode(parent, map);
DOMUtilities.insertBeforeNodeAndWhitespace(parent, node, insertBeforeNode);
indent(node, map);
addDOMAdapter(node); // Hook up listeners
}
return (Element) node;
}
/**
* Remove the node passed in if it has only whitespace nodes as children
*
* @param node
* org.w3c.dom.Node The node to check
*/
protected void removeIfEmpty(Node node) {
NodeList nl = node.getChildNodes();
// Run through all the nodes children. If a non-whitespace node
// pis found, bail.
for (int i = 0; i < nl.getLength(); i++) {
Node childNode = nl.item(i);
if (!DOMUtilities.isWhitespace(childNode))
return;
}
// We only get here if there are no non-whitespace chars, so
// simply remove the node.
removeDOMChild(node.getParentNode(), node);
}
/**
* Remove the DOM path specified by <map>from <node>
*/
protected void removeDOMPath(Node node, Translator map) {
Node childNode = findDOMPath(node, map, false);
while (childNode != null && childNode != node) {
removeIfEmpty(childNode);
childNode = childNode.getParentNode();
}
}
/**
* Create an adapter for a child DOM node
*
* @param node
* org.w3c.dom.Node The node to create the adapter for.
*/
protected EMF2DOMAdapter createAdapter(EObject mofObject, Translator childMap) {
// Assert.isNotNull(childMap.getChildAdapterClass());
Assert.isNotNull(mofObject);
EMF2DOMAdapter adapter = (EMF2DOMAdapter) EcoreUtil.getAdapter(mofObject.eAdapters(), EMF2DOMAdapter.ADAPTER_CLASS);
if (adapter != null && adapter.isMOFProxy()) {
removeAdapters(adapter.getNode());
mofObject.eAdapters().remove(adapter);
adapter = null;
}
if (adapter == null)
adapter = primCreateAdapter(mofObject, childMap);
return adapter;
}
/**
* 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 EMF2DOMAdapterImpl(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 EMF2DOMAdapterImpl(node, 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 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 Element createNewNode(EObject mofObject, Translator childMap) {
Node node = getNode();
Document doc = (node instanceof Document) ? (Document) node : node.getOwnerDocument();
Element element = doc.createElement(childMap.getDOMName(mofObject));
if (childMap.isEmptyTag())
setEmptyTag(element);
return element;
}
protected void setEmptyTag(Element element) {
Revisit.toDo();
//Need to figure out how to do this with pure DOM apis, if it is
// possible
}
/*
* Return true if MOF object is a proxy.
*/
public boolean isMOFProxy() {
if (isRoot || target == null)
return false;
return ((InternalEObject) target).eIsProxy();
}
public EObject getEObject() {
if (isRoot)
return null;
return (EObject) target;
}
/**
* Return the DOM node that the target of this adapter maps to. If the target MOF object maps to
* more than one DOM node, this node is the top-most node.
*/
public Node getNode() {
return fNode;
}
public void setNode(Node aNode) {
fNode = aNode;
}
public void updateDOM() {
if (!isNotificationEnabled())
return;
primUpdateDOM();
}
public void updateMOF() {
if (!isNotificationEnabled())
return;
primUpdateMOF();
}
protected void primUpdateDOM() {
if (isRoot)
updateDOMRootFeature();
else {
Translator[] maps = getChildTranslators();
for (int i = 0; i < maps.length; i++) {
updateDOMFeature(maps[i], getNode(), getEObject());
}
}
}
public void primUpdateMOF() {
if (isRoot)
updateMOFRootFeature();
else {
Translator[] maps = getChildTranslators();
for (int i = 0; i < maps.length; i++) {
updateMOFFeature(maps[i], getNode(), getEObject());
}
}
}
protected void updateDOMRootFeature() {
boolean notificationFlag = isNotificationEnabled();
try {
setNotificationEnabled(false);
primUpdateDOMMultiFeature(fTranslator, fNode, getResourceContents(), getDOMChildren(fNode, fTranslator), null);
updateDOMDocumentType();
} finally {
setNotificationEnabled(notificationFlag);
}
}
protected void updateMOFRootFeature() {
boolean notificationFlag = isNotificationEnabled();
try {
setNotificationEnabled(false);
updateMOFDocumentType();
primUpdateMOFMultiFeature(fTranslator, fNode, getResourceContents(), getDOMChildren(fNode, fTranslator));
} finally {
setNotificationEnabled(notificationFlag);
}
}
protected DocumentType getDocumentType() {
return ((Document) fNode).getDoctype();
}
protected TranslatorResource getResource() {
return (TranslatorResource) getTarget();
}
protected EList getResourceContents() {
if (!isRoot)
throw new IllegalStateException();
return ((Resource) getTarget()).getContents();
}
protected void updateDOMDocumentType() {
DocumentType docType = getDocumentType();
String publicId = null, systemId = null, oldPublicId, oldSystemId;
oldPublicId = docType == null ? null : docType.getPublicId();
oldSystemId = docType == null ? null : docType.getSystemId();
TranslatorResource resource = getResource();
if (resource != null) {
publicId = resource.getPublicId();
systemId = resource.getSystemId();
if (!(StringUtil.stringsEqual(publicId, oldPublicId) && StringUtil.stringsEqual(systemId, oldSystemId)))
fRenderer.replaceDocumentType(resource.getDoctype(), publicId, systemId);
}
}
protected void updateDOMDocumentType(Notification msg) {
if (msg.getFeature() == TranslatorResource.DOC_TYPE_FEATURE)
updateDOMDocumentType();
}
protected void updateMOFDocumentType() {
TranslatorResource resource = getResource();
if (resource == null)
return;
String publicId = null, systemId = null;
DocumentType docType = getDocumentType();
if (docType != null) {
publicId = docType.getPublicId();
systemId = docType.getSystemId();
}
if ((!(StringUtil.stringsEqual(publicId, resource.getPublicId()) && StringUtil.stringsEqual(systemId, resource.getSystemId()))) ||
((resource.getPublicId() == null) || resource.getSystemId() == null))
resource.setDoctypeValues(publicId, systemId);
}
@Override
public void notifyChanged(Notification msg) {
if (isRoot) {
notifyChangedForRoot(msg);
return;
}
if (isDependencyFeature(msg))
handleDependencyFeature(msg);
if (!isNotificationEnabled())
return;
debugMOFNotify(msg);
switch (msg.getEventType()) {
case Notification.ADD :
case Notification.REMOVE :
case Notification.ADD_MANY :
case Notification.REMOVE_MANY :
case Notification.SET :
case Notification.UNSET :
case Notification.MOVE :
Translator translator = findTranslator(msg);
if (translator == null)
translator = addVariableTranslatorIfNecessary(msg);
if (translator != null)
updateDOMFeature(translator, getNode(), getEObject());
break;
}
}
protected void notifyChangedForRoot(Notification msg) {
if (!isNotificationEnabled())
return;
debugMOFNotify(msg);
switch (msg.getEventType()) {
case Notification.ADD :
case Notification.REMOVE :
case Notification.ADD_MANY :
case Notification.REMOVE_MANY :
primUpdateDOM();
break;
case Notification.SET :
updateDOMDocumentType(msg);
break;
case EtoolsCopySession.RESOURCE_COPIED :
updateDOM();
break;
}
}
protected void addDependencyAdapter(EObject child) {
Adapter existing = EcoreUtil.getExistingAdapter(child, DependencyAdapter.KEY);
if (existing != null)
return;
DependencyAdapter forwarder = new DependencyAdapter();
child.eAdapters().add(forwarder);
forwarder.setTarget(child);
}
protected void addDOMAdapter() {
primAddDOMAdapter(fNode, this);
if (fDebug) {
EcoreUtilitiesPlugin.logError("Adding DOM adapter: " + this); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tto: " + fNode); //$NON-NLS-1$
}
// Go through the maps. All of the DOM nodes that are not listened
// to by another DOM Node adapter, must be listened to by this adapter.
NodeList childNodes = fNode.getChildNodes();
for (int j = 0; j < childNodes.getLength(); j++) {
Node childNode = childNodes.item(j);
int nodeType = childNode.getNodeType();
if (!DOMUtilities.isTextNode(childNode) && nodeType != Node.COMMENT_NODE) {
Translator map = findTranslator(childNode.getNodeName(), false);
if (map != null && map.isManagedByParent())
addDOMAdapter(childNode);
}
}
}
protected Translator addVariableTranslatorIfNecessary(Notification msg) {
VariableTranslatorFactory fact = fTranslator.getVariableTranslatorFactory();
Translator trans = null;
if (fact != null && fact.accepts(msg)) {
trans = fact.create(msg);
if (trans != null)
childTranslators = (Translator[]) Translator.concat(childTranslators, trans);
}
return trans;
}
protected Text createTextNode(Document doc, Translator map, String text) {
String nonnulltext = (text != null) ? text : ""; //$NON-NLS-1$
return map.isCDATAContent() ? doc.createCDATASection(nonnulltext) : doc.createTextNode(nonnulltext);
}
protected String extractReadAheadName() {
if (!fTranslator.hasReadAheadNames())
return null;
String readAheadName = null;
ReadAheadHelper helper = fTranslator.getReadAheadHelper(fNode.getNodeName());
if (helper == null)
return null;
Node child = null;
String[] names = helper.getValues();
if (helper.getChildDOMName() == null) {
for (int i = 0; i < names.length; i++) {
child = DOMUtilities.getNodeChild(fNode, names[i]);
if (child != null) {
readAheadName = names[i];
break;
}
}
} else {
child = DOMUtilities.getNodeChild(fNode, helper.getChildDOMName());
if (child != null)
readAheadName = DOMUtilities.getChildText(child);
}
if (readAheadName == null)
readAheadName = names[0];
return readAheadName;
}
/**
* Extracts the text from <node>and converts it to an object suitable for setting into <feature>
*/
protected Object extractValue(Node node, Translator map, EObject emfObject) {
// Extract the value from the text child
Node textNode = DOMUtilities.getChildTextNode(node);
String trimmedValue = null;
if (textNode != null)
trimmedValue = textNode.getNodeValue();
try {
return map.convertStringToValue(trimmedValue, emfObject);
} catch (FeatureValueConversionException ex) {
handleFeatureValueConversionException(ex);
return null;
}
}
/**
* @param ex
*/
protected void handleFeatureValueConversionException(FeatureValueConversionException ex) {
throw ex;
}
/*
* Finds the child node that <map> specifies. If there is more than one child that satisfies
* <map> then the first one is returned.
*
* This method traverses past the domPath if one is specified in the <map>.
*/
protected Node findDOMNode(Node parent, Translator map) {
return findDOMNode(parent, map, false);
}
/*
* Finds the child node that <map> specifies. If there is more than one child that satisfies
* <map> then the first one is returned. The <addAdapters> boolean is used to determine if the
* receiver is added as an adapter to every node found on the path.
*
* This method traverses past the domPath if one is specified in the <map>.
*/
protected Node findDOMNode(Node parent, Translator map, boolean addAdapters) {
// First, trace down the path
Node curNode = findDOMPath(parent, map, addAdapters);
if (map.isDOMTextValue() || map.isDOMAttribute() || curNode == null)
return curNode;
// Now look for the first DOM name we can find
String[] domNames = map.getDOMNames();
Node node = null;
for (int i = 0; i < domNames.length; i++) {
String nodeName = domNames[i];
List nodes = DOMUtilities.getNodeChildren(curNode, nodeName);
if (nodes != null && !nodes.isEmpty()) {
if (nodes.size() > 1)
handleInvalidMultiNodes(nodeName);
node = (Node) nodes.get(0);
if (node != null) {
if (addAdapters)
addDOMAdapter(curNode);
break;
}
}
}
return node;
}
protected void handleInvalidMultiNodes(String nodeName) {
throw new IllegalStateException(WFTUtilsResourceHandler.getString(WFTUtilsResourceHandler.EMF2DOMAdapterImpl_ERROR_0, new Object[]{nodeName}));
}
/**
* Creates the path specified by <map>under <node>. Only the portion of the path that does not
* exist (if any) is created
*
* @param node
* org.w3c.dom.Node
* @param map
* com.ibm.etools.mof2dom.Translator
*/
protected Text findOrCreateTextNode(Node parent, Translator map, String text) {
Text textNode = DOMUtilities.getChildTextNode(parent);
if (textNode != null) {
textNode.setData(text);
} else {
textNode = createTextNode(parent.getOwnerDocument(), map, text);
if (!isEmptyTag((Element) parent)) {
DOMUtilities.insertBeforeNode(parent, textNode, null);
}
else
{
parent.appendChild(textNode);
}
}
return textNode;
}
protected Translator findTranslator(String tagName, boolean attributeMap) {
Translator[] maps = getChildTranslators();
for (int i = 0; i < maps.length; i++) {
Translator map = maps[i];
if (map.isMapFor(tagName) && attributeMap == map.isDOMAttribute())
return maps[i];
}
return null;
}
protected EMF2DOMAdapter getExistingAdapter(Node aNode) {
EMF2DOMAdapter adapter = primGetExistingAdapter(aNode);
if (adapter != null && adapter.isMOFProxy()) {
removeDOMAdapter(aNode, adapter);
adapter.getTarget().eAdapters().remove(adapter);
adapter = null;
}
return adapter;
}
protected void handleDependencyFeature(Notification msg) {
if (msg.getOldValue() != null)
removeDependencyAdapter((EObject) msg.getOldValue());
if (msg.getNewValue() != null)
addDependencyAdapter((EObject) msg.getNewValue());
}
protected boolean isDependencyFeature(Notification msg) {
switch (msg.getEventType()) {
case Notification.SET :
return fTranslator.isDependencyParent() && fTranslator.getDependencyFeature() == msg.getFeature();
default :
return false;
}
}
protected boolean isEmptyTag(Element parent) {
Revisit.toDo();
//Determine how to implement this with pure DOM apis, if possible.
return false;
}
protected void postUpdateDOMFeature(Translator map, Node node, EObject mofObject) {
}
protected void preUpdateDOMFeature(Translator map, Node node, EObject mofObject) {
}
/**
* Update an attribute of the target DOM object from with the values currently stored in the MOF
* object. The
*
* @map specifies the name of the MOF attribute to update and the name of the DOM node.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator
*/
protected void primUpdateDOMFeature(Translator map, Node node, EObject mofObject) {
Object attrValue = null;
boolean isSet = false;
if (map.isIDMap()) {
try {
attrValue = map.getMOFValue(mofObject);
} catch (IDTranslator.NoResourceException ex) {
//If the object has been removed from the resource,
//No need to update
return;
}
isSet = attrValue != null;
} else {
attrValue = map.getMOFValue(mofObject);
isSet = map.isSetMOFValue(mofObject);
}
if (map.isDOMAttribute()) {
// An attribute of the MOF object maps to an attribute of the
// DOM node. Get the value of the MOF attribute and set it
// into DOM node.
Element e = (Element) createDOMPath(node, map);
if (attrValue != null && isSet)
e.setAttribute(map.getDOMName(mofObject), map.convertValueToString(attrValue, mofObject));
else
e.removeAttribute(map.getDOMName(mofObject));
} else {
updateDOMSubtree(map, node, mofObject, attrValue);
}
}
/**
* Update an attribute of the target DOM object from with the values currently stored in the MOF
* object. The
*
* @map specifies the name of the MOF attribute to update and the name of the DOM node.
*/
protected void primUpdateDOMLinkFeature(Translator map, Node node, EObject mofObject) {
LinkUpdaterTarget.INSTANCE.updateDOM(map, node, mofObject);
}
/**
* Update all the children of the target MOF object in the relationship described by
*
* @map.
*/
protected void primUpdateDOMMultiFeature(Translator map, Node node, EObject mofObject) {
List mofChildren = map.getMOFChildren(mofObject);
List domChildren = getDOMChildren(node, map);
primUpdateDOMMultiFeature(map, node, mofChildren, domChildren, mofObject);
}
/**
* Update an attribute of the target MOF object from the DOM node subtree. The
*
* @map specifies the name of the MOF attribute to update and the name of the DOM node.
*/
protected boolean primUpdateMOFFeature(Translator map, Node node, EObject mofObject) {
if (!map.featureExists(mofObject))
return false;
Object domValue = null;
boolean updateMOFAttAdapter = false;
boolean isUnset = false;
EMF2DOMAdapter attrAdapter = null;
Node child = findDOMNode(node, map, true);
if (map.isDOMAttribute() && child != null) {
// An attribute of the MOF object maps to an attribute of the
// DOM node. Get the value of the DOM attribute and set it
// into the MOF object.
Attr domAttr = (Attr) child.getAttributes().getNamedItem(map.getDOMName(mofObject));
if (domAttr != null) {
domValue = domAttr.getValue();
try {
domValue = map.convertStringToValue((String) domValue, mofObject);
} catch (FeatureValueConversionException e) {
handleFeatureValueConversionException(e);
domValue = null;
}
} else
isUnset = true;
} else {
// An attribute of the MOF object is actually a sub-element
// of the DOM node. Search for the first sub-element with
// the correct name to use as an attribute.
if (child != null) {
// Check to see if this is a single valued attribute that has
// a MOF object as its value
if (!map.isManagedByParent()) {
attrAdapter = createAdapter(child, map);
updateMOFAttAdapter = true;
domValue = attrAdapter.getTarget();
} else {
// Check to make sure the child is adapted. If not, adapt
// it.
addDOMAdapter(child);
// Extract the value from the text child
domValue = extractValue(child, map, mofObject);
}
} else
isUnset = true;
}
// Set the attribute extracted from the DOM to the MOF object.
boolean hasChanged = true;
try {
if (map.isIDMap())
map.setMOFValue(mofObject, domValue);
else {
Object oldValue = null;
oldValue = map.getMOFValue(mofObject);
boolean isSet = map.isSetMOFValue(mofObject);
//In the case of enums with default values, we need to trip
// the attribute from
//default to a set value
if (oldValue == domValue) {
if (oldValue == null || isSet)
hasChanged = false;
} else if (domValue == null && !isSet)
//If the domValue is null and the feature is not set, then
// we don't need
//to do anything
hasChanged = false;
else if (oldValue != null && oldValue.equals(domValue) && isSet)
hasChanged = false;
if (oldValue == null && domValue == null && map.isSetMOFValue(mofObject) == isUnset)
hasChanged = true;
if (hasChanged) {
if (!(map.isDataType()) && !map.isShared())
removeMOFValue((EObject) oldValue, map);
if (domValue == null)
map.unSetMOFValue(mofObject);
else
map.setMOFValue(mofObject, domValue);
if ((domValue == null && !(map.isEnumFeature())) || isUnset)
map.unSetMOFValue(mofObject); //unset
// null
// for
// non
// enum
// features
if (updateMOFAttAdapter && attrAdapter != null)
attrAdapter.updateMOF();
}
}
} catch (FeatureValueConversionException ex) {
handleFeatureValueConversionException(ex);
map.unSetMOFValue(mofObject);
}
return hasChanged;
}
/**
* Update an attribute of the target MOF object from the DOM node subtree. This method is only
* called if the DOM node changes and the map is an object link map. The
*
* @map specifies the name of the MOF attribute to update and the name of the DOM node.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator
* @return Return true if the MOF feature was updated, false if no update was done.
*/
protected void primUpdateMOFLinkFeature(Translator map, Node node, EObject mofObject) {
LinkUpdaterTarget.INSTANCE.updateMOF(map, node, mofObject);
}
/**
* Update all the children of the target MOF object in the relationship described by
*
* @map.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator Describes the mapping from the MOF
* attribute name to the DOM node name
*/
protected void primUpdateMOFMultiFeature(Translator map, Node node, EObject mofObject) {
// If the feature is a collection of strings or ints, call a special
// method
// that handles this.
if (map.isManagedByParent()) {
updateMOFMultiPrimitiveFeature(map, node, mofObject);
return;
}
List nodeChildren = getDOMChildren(node, map);
List mofChildren = map.getMOFChildren(mofObject);
primUpdateMOFMultiFeature(map, node, mofChildren, nodeChildren);
}
protected void removeDependencyAdapter(EObject obj) {
Adapter existing = EcoreUtil.getExistingAdapter(obj, DependencyAdapter.KEY);
if (existing != null)
obj.eAdapters().remove(existing);
}
/**
* Removes all the DOM children from <parent>that are represented by <map>.
*/
protected void removeDOMChildren(Node parent, Translator map) {
String[] domNames = map.getDOMNames();
HashSet domNamesSet = new HashSet(domNames.length);
for (int i = 0; i < domNames.length; i++)
domNamesSet.add(domNames[i]);
// Walk through all the children and find any that match the map.
NodeList nl = parent.getChildNodes();
List toRemove = new ArrayList();
for (int i = 0; i < nl.getLength(); i++) {
Node childNode = nl.item(i);
if (domNamesSet.contains(childNode.getNodeName()))
toRemove.add(childNode);
}
// Remove any children that were found.
for (int i = 0; i < toRemove.size(); i++) {
Node childNode = (Node) toRemove.get(i);
removeDOMChild(parent, childNode, true);
}
}
protected void setTargetFromNode() {
setTarget(fTranslator.createEMFObject(fNode.getNodeName(), extractReadAheadName()));
}
/**
* Update an attribute of the target DOM object from with the values currently stored in the MOF
* object. The
*
* @map specifies the name of the MOF attribute to update and the name of the DOM node.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator
*/
final public void updateDOMFeature(Translator map, Node node, EObject mofObject) {
if (!isNotificationEnabled())
return;
try {
preUpdateDOMFeature(map, node, mofObject);
if (map.isMultiValued()) {
updateDOMMultiFeature(map, node, mofObject);
return;
}
if (fDebug) {
EcoreUtilitiesPlugin.logError("Updating DOM Node: " + node); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tfrom: " + mofObject); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tmap : " + map); //$NON-NLS-1$
}
boolean notificationFlag = isNotificationEnabled();
try {
setNotificationEnabled(false);
primUpdateDOMFeature(map, node, mofObject);
} finally {
setNotificationEnabled(notificationFlag);
}
if (map.isTargetLinkMap()) {
updateDOMLinkFeature(map, node, mofObject);
}
} finally {
postUpdateDOMFeature(map, node, mofObject);
}
}
/**
* Update an attribute of the target DOM object from the values currently stored in the MOF
* object. The
*
* @map specifies the name of the MOF attribute to update and the name of the DOM node.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator
*/
final protected void updateDOMLinkFeature(Translator map, Node node, EObject mofObject) {
if (fDebug) {
EcoreUtilitiesPlugin.logError("Updating DOM Node (link): " + node); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tfrom: " + mofObject); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tmap : " + map); //$NON-NLS-1$
}
primUpdateDOMLinkFeature(map, node, mofObject);
}
/**
* Update all the children of the target MOF object in the relationship described by
*
* @map.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator Describes the mapping from the MOF
* attribute name to the DOM node name
*/
final protected void updateDOMMultiFeature(Translator map, Node node, EObject mofObject) {
if (fDebug) {
EcoreUtilitiesPlugin.logError("Updating DOM Node (multi): " + node); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tfrom: " + mofObject); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tmap : " + map); //$NON-NLS-1$
}
boolean notificationFlag = isNotificationEnabled();
try {
setNotificationEnabled(false);
primUpdateDOMMultiFeature(map, node, mofObject);
} finally {
setNotificationEnabled(notificationFlag);
}
}
/**
* Update a DOM subtree to reflect the mofObject and map passed in. The subtree is of <node>is
* updated.
*/
protected void updateDOMSubtree(Translator map, Node node, EObject mofObject, Object attrValue) {
Object innerAttrValue = attrValue;
if (map.featureExists(mofObject)) {
if ((map.isEnumFeature() || map.isBooleanFeature()) && (map.isUnsettable() && !map.isSetMOFValue(mofObject)))
innerAttrValue = null;
} else
innerAttrValue = map.extractStringValue(mofObject);
// Create and/or update the DOM subtree
if (innerAttrValue != null) {
Node parent = createDOMPath(node, map);
if (map.isManagedByParent()) {
// Handle the case where the mof value is not another
// mof object (primitive)
if (map.getDOMName(mofObject) != null && map.getDOMName(mofObject).startsWith("#")) //$NON-NLS-1$
return;
Element child = map.isDOMTextValue() ? (Element) parent : findOrCreateNode(parent, map, map.getDOMName(mofObject));
findOrCreateTextNode(child, map, map.convertValueToString(innerAttrValue, mofObject));
} else {
// Handle the case were the mof value is a mof object.
EObject mofValue = (EObject) innerAttrValue;
EMF2DOMAdapter valueAdapter = (EMF2DOMAdapter) EcoreUtil.getExistingAdapter(mofValue, EMF2DOMAdapter.ADAPTER_CLASS);
if (valueAdapter != null)
valueAdapter.updateDOM();
else {
removeDOMChildren(parent, map);
EMF2DOMAdapter adapter = createAdapter(mofValue, map);
List mofChildren = map.getMOFChildren(mofObject);
List domChildren = getDOMChildren(parent, map);
Node insertBeforeNode = findInsertBeforeNode(parent, map, mofChildren, 0, domChildren);
DOMUtilities.insertBeforeNodeAndWhitespace(parent, adapter.getNode(), insertBeforeNode);
boolean notificationFlag = adapter.isNotificationEnabled();
adapter.setNotificationEnabled(false);
try {
indent(adapter.getNode(), map);
} finally {
adapter.setNotificationEnabled(notificationFlag);
}
adapter.updateDOM();
}
}
} else {
// The attribute value was set to null or unset. Remove any
// existing DOM nodes.
Node child = findDOMNode(node, map);
if (child != null)
removeDOMChild(child.getParentNode(), child);
}
}
/**
* Update a feature that is set by linking to another existing object. This method is called
* when the MOF object is updated in order to update DOM nodes.
*/
final protected void updateMOFLinkFeature(Translator map, Node node, EObject mofObject) {
if (fDebug) {
EcoreUtilitiesPlugin.logError("Updating MOFObject (link): " + mofObject); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tfrom: " + node); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tmap : " + map); //$NON-NLS-1$
}
boolean notificationFlag = isNotificationEnabled();
try {
setNotificationEnabled(false);
primUpdateMOFLinkFeature(map, node, mofObject);
} finally {
setNotificationEnabled(notificationFlag);
}
}
/**
* Update all the children of the target MOF object in the relationship described by
*
* @map.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator Describes the mapping from the MOF
* attribute name to the DOM node name
*/
final protected void updateMOFMultiFeature(Translator map, Node node, EObject mofObject) {
if (fDebug) {
EcoreUtilitiesPlugin.logError("Updating MOFObject (multi): " + mofObject); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tfrom: " + node); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tmap : " + map); //$NON-NLS-1$
}
boolean notificationFlag = isNotificationEnabled();
try {
setNotificationEnabled(false);
primUpdateMOFMultiFeature(map, node, mofObject);
} finally {
setNotificationEnabled(notificationFlag);
}
}
/**
* Update all the children of the target MOF object in the relationship described by
*
* @map. The relationship MUST BE a collection of string for this method to work.
*/
protected void updateMOFMultiPrimitiveFeature(Translator map, Node node, EObject mofObject) {
List nodeChildren = getDOMChildren(node, map);
map.clearList(mofObject);
// Go through the list of nodes and update the MOF collection
int addIndex = 0;
for (int i = 0; i < nodeChildren.size(); i++) {
Node child = (Node) nodeChildren.get(i);
Object attributeValue = extractValue(child, map, mofObject);
boolean advanceAddIndex = true;
if (attributeValue != null){
if(map.getFeature() != null && map.getFeature().isUnique() && mofObject.eGet(map.getFeature()) != null && mofObject.eGet(map.getFeature()) instanceof List && ((List) mofObject.eGet(map.getFeature())).contains(attributeValue)){
advanceAddIndex = false;
String domName = map.domNameAndPath != null ? map.domNameAndPath : "attribute"; //$NON-NLS-1$
EcoreUtilitiesPlugin.logError(new IllegalArgumentException("The 'no duplicates' constraint is violated by "+domName+" = "+attributeValue)); //$NON-NLS-1$ //$NON-NLS-2$
handleInvalidMultiNodes(child.getNodeName());
} else {
map.setMOFValue(mofObject, attributeValue, addIndex);
}
if(advanceAddIndex){
addIndex ++;
}
}
// Adapt the node so update will occur.
addDOMAdapter(child);
}
if (map.hasDOMPath() && nodeChildren.isEmpty() && findDOMPath(node, map, false) != null)
map.setMOFValueFromEmptyDOMPath(mofObject);
}
/**
* Update an attribute of the target MOF object from the DOM node subtree. The
*
* @map specifies the name of the MOF attribute to update and the name of the DOM node.
*
* @param map
* com.ibm.etools.mof2dom.AttributeTranslator
*/
public void updateMOFFeature(Translator map, Node node, EObject mofObject) {
if (!isNotificationEnabled())
return;
if (map.isMultiValued()) {
updateMOFMultiFeature(map, node, mofObject);
return;
} else if (map.isComment()) {
updateMOFCommentFeature(map, node, mofObject);
return;
}
// TODO MDE Add a map.isComment() and updateMOFCommentFeature(map, node, mofObject);
if (fDebug) {
EcoreUtilitiesPlugin.logError("Updating MOFObject: " + mofObject); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tfrom: " + node); //$NON-NLS-1$
EcoreUtilitiesPlugin.logError("\tmap : " + map); //$NON-NLS-1$
}
boolean notificationFlag = isNotificationEnabled();
boolean hasChanged = false;
try {
setNotificationEnabled(false);
hasChanged = primUpdateMOFFeature(map, node, mofObject);
} finally {
setNotificationEnabled(notificationFlag);
}
if (map.isTargetLinkMap() && hasChanged)
updateMOFLinkFeature(map, node, mofObject);
}
/**
* @param map
* @param node
* @param mofObject
*/
public void updateMOFCommentFeature(Translator map, Node node, EObject mofObject) {
Node commentNode = node;
/* scan up the dom to find the first comment node before this node */
while ((commentNode = commentNode.getPreviousSibling()) != null && commentNode.getNodeType() != Node.COMMENT_NODE) {
/* no comment available */
if (commentNode.getNodeType() == Node.ELEMENT_NODE)
return;
}
if (commentNode != null)
map.setMOFValue(mofObject, commentNode.getNodeValue());
}
}