| /******************************************************************************* |
| * 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); |
| } |
| } |
| } |