blob: b857bda089d5a2cea3720846406ee85938778108 [file] [log] [blame]
/*******************************************************************************
* 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 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Sonatype, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.m2e.model.edit.pom.translators;
import java.util.List;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
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.IDOMNode;
import org.eclipse.m2e.model.edit.pom.PomFactory;
/**
* Translates a multi valued feature into a <foos> <foo>bar</foo> </foos> structure.
*
* @author Mike Poindexter
*/
@SuppressWarnings("restriction")
public class ListAdapter extends TranslatorAdapter {
protected List list;
private EClass elementType;
public ListAdapter(SSESyncResource resc, Element containerNode, List<?> list, EClass elementType) {
super(resc);
this.node = containerNode;
this.elementType = elementType;
this.list = list;
this.resource = resc;
}
public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue,
Object newValue, int pos) {
if(resource.isProcessEvents()) {
try {
resource.setProcessEvents(false);
if(INodeNotifier.ADD == eventType && newValue instanceof Element) {
if(notifier == node) {
IDOMElement addedElement = (IDOMElement) newValue;
int idx = absoluteIndexOf(node, addedElement);
if(idx == -1)
idx = 0;
list.add(idx, getObject(addedElement, true));
}
} else if(INodeNotifier.REMOVE == eventType && oldValue instanceof Element) {
if(notifier == node) {
// Remove the corresponding object from the model.
Object o = getObject((Element) oldValue, false);
if(o instanceof String && o.toString().length() == 0) {
// the removed oldValue has unfortunately no text value in it, so no way to identify the
// remove entry.
// -> just remove all and add the ones that stayed around.
// only after checking that the object to be removed is a string, just to be sure..
NodeList lst = node.getChildNodes();
list.clear();
for(int i = 0; i < lst.getLength(); i++ ) {
Node nd = lst.item(i);
if(nd instanceof Element) {
list.add(getElementText((Element) nd));
}
}
} else if(o != null) {
list.remove(o);
}
// TODO: What to do here? We don't know which model
// child
// to remove. I don't think this can happen. -MDP
}
} else if(changedFeature instanceof Text && elementType == null) {
if(notifier != node && notifier instanceof Element) {
Element e = (Element) notifier;
int idx = absoluteIndexOf(node, e);
if(idx < 0)
idx = 0;
list.remove(idx);
list.add(idx, getObject(e, true));
}
}
} finally {
resource.setProcessEvents(true);
}
}
}
public void add(Object newValue, int position) {
Object value = getElementValue(newValue);
if(value instanceof EObject) {
EObject eo = (EObject) value;
if(EcoreUtil.getAdapter(eo.eAdapters(), ModelObjectAdapter.class) == null) {
String tagName = getElementName(newValue);
Element newElement = node.getOwnerDocument().createElement(tagName);
Node before = getNthChildWithName(node, "*", position); //$NON-NLS-1$
if(before != null) {
node.insertBefore(newElement, before);
} else {
node.appendChild(newElement);
}
ModelObjectAdapter newAdapter = new ModelObjectAdapter(resource, eo, newElement);
eo.eAdapters().add(newAdapter);
formatNode(newElement);
((IDOMNode) newElement).addAdapter(newAdapter);
newAdapter.save();
}
} else {
String tagName = getElementName(newValue);
Element newElement = node.getOwnerDocument().createElement(tagName);
org.w3c.dom.Text text = node.getOwnerDocument().createTextNode(value.toString());
newElement.appendChild(text);
Node before = getNthChildWithName(node, "*", position); //$NON-NLS-1$
if(before != null) {
node.insertBefore(newElement, before);
} else {
node.appendChild(newElement);
}
formatNode(newElement);
((IDOMNode) newElement).addAdapter(this);
}
}
public void remove(Object oldValue, int position) {
if(position == -1) {
position = 0;
}
Element n = getNthChildWithName(node, "*", position); //$NON-NLS-1$
if(n != null)
removeChildElement(n);
}
@Override
public void update(Object oldValue, Object newValue, int index) {
remove(oldValue, index);
add(newValue, index);
}
protected String getElementName(Object o) {
String name = node.getLocalName();
if(name.endsWith("ies")) //$NON-NLS-1$
name = name.replaceAll("ies$", "y"); //$NON-NLS-1$ //$NON-NLS-2$
else
name = name.replaceAll("s$", ""); //$NON-NLS-1$ //$NON-NLS-2$
return name;
}
protected Object getElementValue(Object o) {
return o;
}
protected Object getObject(Element childElement, boolean createIfNeeded) {
if(elementType == null) {
ListAdapter existing = (ListAdapter) ((IDOMNode) childElement).getExistingAdapter(ListAdapter.class);
if(existing == null) {
((IDOMNode) childElement).addAdapter(this);
}
return getElementText(childElement);
} else {
ModelObjectAdapter existing = (ModelObjectAdapter) ((IDOMNode) childElement)
.getExistingAdapter(ModelObjectAdapter.class);
if(existing == null) {
if(createIfNeeded) {
EObject eo = PomFactory.eINSTANCE.create(elementType);
existing = new ModelObjectAdapter(resource, eo, childElement);
eo.eAdapters().add(existing);
((IDOMNode) childElement).addAdapter(existing);
existing.load();
return eo;
} else {
return null;
}
} else {
return existing.getTarget();
}
}
}
public boolean isAdapterForType(Object type) {
return ListAdapter.class.equals(type);
}
@Override
public void load() {
//MNGECLIPSE-2345, MNGECLIPSE-2694 when load is called on a list adapter already containing items,
// the old items shall be discarded to avoid duplicates.
list.clear();
NodeList children = node.getChildNodes();
int nChildren = children.getLength();
for(int i = 0; i < nChildren; i++ ) {
Node child = children.item(i);
if(child instanceof Element) {
list.add(getObject((Element) child, true));
}
}
}
@Override
public void save() {
for(Object o : list) {
add(o, -1);
}
}
}