/******************************************************************************* * Copyright (c) 2001, 2011 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 * Jens Lukowski/Innoopract - initial renaming/restructuring * *******************************************************************************/ package org.eclipse.wst.xml.ui.internal.properties; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Stack; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.views.properties.IPropertyDescriptor; import org.eclipse.ui.views.properties.IPropertySheetEntry; import org.eclipse.ui.views.properties.IPropertySource; import org.eclipse.ui.views.properties.IPropertySource2; import org.eclipse.ui.views.properties.TextPropertyDescriptor; import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; import org.eclipse.wst.sse.ui.views.properties.IPropertySourceExtension; import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration; import org.eclipse.wst.xml.core.internal.contentmodel.CMDataType; import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap; import org.eclipse.wst.xml.core.internal.contentmodel.CMNode; import org.eclipse.wst.xml.core.internal.contentmodel.basic.CMNamedNodeMapImpl; import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery; import org.eclipse.wst.xml.core.internal.contentmodel.util.DOMNamespaceHelper; import org.eclipse.wst.xml.core.internal.document.DocumentTypeAdapter; import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.eclipse.wst.xml.ui.internal.Logger; import org.eclipse.wst.xml.ui.internal.XMLUIMessages; import org.w3c.dom.Attr; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * An IPropertySource implementation for a JFace viewer used to display * properties of DOM nodes. */ public class XMLPropertySource implements IPropertySource, IPropertySourceExtension, IPropertySource2 { protected final static String CATEGORY_ATTRIBUTES = XMLUIMessages.XMLPropertySourceAdapter_0; /** * Controls whether optional attributes are marked as for "experts" */ private static final boolean fSetExpertFilter = false; /** * Controls whether to derive categories from CMDataTypes; disabled by * default until display strings can be planned */ private boolean fShouldDeriveCategories = false; private final static boolean fSortEnumeratedValues = true; /** * Note: we want the default fCaseSensitive to be true, but, to avoid * meaningless double initialization, we leave default here, and set in * constructor only. */ private boolean fCaseSensitive; private IPropertyDescriptor[] fDescriptors = null; private Node fNode = null; private Stack fValuesBeingSet = new Stack(); public XMLPropertySource(INodeNotifier target) { super(); fNode = initNode(target); fCaseSensitive = initCaseSensitive(fNode); } public XMLPropertySource(Node target, boolean useCategories) { super(); initNode(target); fNode = target; fCaseSensitive = initCaseSensitive(fNode); fShouldDeriveCategories = useCategories; } /** Separate method just to isolate error processing */ private static INodeNotifier initNode(Node target) { if (target instanceof INodeNotifier) { return (INodeNotifier) target; } throw new IllegalArgumentException("XMLPropertySource is only for INodeNotifiers"); //$NON-NLS-1$ } /** Separate method just to isolate error processing */ private static Node initNode(INodeNotifier target) { if (target instanceof Node) { return (Node) target; } throw new IllegalArgumentException("XMLPropertySource is only for W3C DOM Nodes"); //$NON-NLS-1$ } private boolean initCaseSensitive(Node node) { // almost all tags are case sensitive, except that old HTML boolean caseSensitive = true; if (node instanceof IDOMNode) { DocumentTypeAdapter adapter = getDocTypeFromDOMNode(node); if (adapter != null) { caseSensitive = (adapter.getTagNameCase() == DocumentTypeAdapter.STRICT_CASE); } } return caseSensitive; } /** * by "internal spec" the DOCTYPE adapter is only available from Document * Node * * @return {@link DocumentTypeAdapter} */ private DocumentTypeAdapter getDocTypeFromDOMNode(Node node) { DocumentTypeAdapter adapter = null; Document ownerDocument = node.getOwnerDocument(); if (ownerDocument == null) { // if ownerDocument is null, then fNode must be the Document Node // [old, old comment] // hmmmm, guess not. See // https://bugs.eclipse.org/bugs/show_bug.cgi?id=130233 // guess this is used for many INodeNotifiers, not just XML. // (and DTD's use IDOMNode? ... that doesn't sound quite right // ... but, maybe a separate issue). if (node instanceof Document) { ownerDocument = (Document) node; } } if (ownerDocument != null) { adapter = (DocumentTypeAdapter) ((INodeNotifier) ownerDocument).getAdapterFor(DocumentTypeAdapter.class); } return adapter; } private String[] _getValidFixedStrings(CMAttributeDeclaration attrDecl, CMDataType helper) { String attributeName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); List values = new ArrayList(1); String impliedValue = helper.getImpliedValue(); if (impliedValue != null) { values.add(impliedValue); } boolean checkIfCurrentValueIsIncluded = ((fNode.getAttributes() != null) && (fNode.getAttributes().getNamedItem(attributeName) != null) && (fNode.getAttributes().getNamedItem(attributeName).getNodeValue() != null)); if (checkIfCurrentValueIsIncluded) { String currentValue = null; currentValue = fNode.getAttributes().getNamedItem(attributeName).getNodeValue(); if (!currentValue.equals(impliedValue)) { values.add(currentValue); } } String[] validStrings = new String[values.size()]; validStrings = (String[]) values.toArray(validStrings); return validStrings; } private String[] _getValidStrings(CMAttributeDeclaration attrDecl, CMDataType valuesHelper) { String attributeName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); List values = new ArrayList(1); boolean currentValueKnown = false; boolean checkIfCurrentValueIsKnown = ((fNode.getAttributes() != null) && (fNode.getAttributes().getNamedItem(attributeName) != null) && (fNode.getAttributes().getNamedItem(attributeName).getNodeValue() != null)); String currentValue = null; if (checkIfCurrentValueIsKnown) { currentValue = fNode.getAttributes().getNamedItem(attributeName).getNodeValue(); } if ((valuesHelper.getImpliedValueKind() == CMDataType.IMPLIED_VALUE_FIXED) && (valuesHelper.getImpliedValue() != null)) { // FIXED value currentValueKnown = (currentValue != null) && valuesHelper.getImpliedValue().equals(currentValue); values.add(valuesHelper.getImpliedValue()); } else { // ENUMERATED values String[] valueStrings = null; // valueStrings = valuesHelper.getEnumeratedValues(); ModelQuery modelQuery = ModelQueryUtil.getModelQuery(fNode.getOwnerDocument()); if ((modelQuery != null) && (fNode.getNodeType() == Node.ELEMENT_NODE)) { valueStrings = modelQuery.getPossibleDataTypeValues((Element) fNode, attrDecl); } else { valueStrings = attrDecl.getAttrType().getEnumeratedValues(); } if (valueStrings != null) { for (int i = 0; i < valueStrings.length; i++) { if (checkIfCurrentValueIsKnown && valueStrings[i].equals(currentValue)) { currentValueKnown = true; } values.add(valueStrings[i]); } } } if ((valuesHelper.getImpliedValueKind() != CMDataType.IMPLIED_VALUE_NONE) && (valuesHelper.getImpliedValue() != null)) { if (!values.contains(valuesHelper.getImpliedValue())) { values.add(valuesHelper.getImpliedValue()); } } if (checkIfCurrentValueIsKnown && !currentValueKnown && (currentValue != null) && (currentValue.length() > 0)) { values.add(currentValue); } String[] validStrings = new String[values.size()]; validStrings = (String[]) values.toArray(validStrings); return validStrings; } private IPropertyDescriptor createDefaultPropertyDescriptor(String attributeName) { return createDefaultPropertyDescriptor(attributeName, false); } private IPropertyDescriptor createDefaultPropertyDescriptor(String attributeName, boolean hideOnFilter) { // The descriptor class used here is also used in // updatePropertyDescriptors() TextPropertyDescriptor descriptor = new TextPropertyDescriptor(attributeName, attributeName); descriptor.setCategory(getCategory(null, null)); descriptor.setDescription(attributeName); if (hideOnFilter && fSetExpertFilter) { descriptor.setFilterFlags(new String[]{IPropertySheetEntry.FILTER_ID_EXPERT}); } return descriptor; } /** * Creates a property descriptor for an attribute with ENUMERATED values - * if the value does not exist, an editable combo box is returned - if the * value exists but is not one in the enumerated list of value, a combo * box featuring the current and correct values is returned - if the value * exists and it is a valid value, a combo box featuring the correct * values with the current one visible is returned */ private IPropertyDescriptor createEnumeratedPropertyDescriptor(CMAttributeDeclaration attrDecl, CMDataType valuesHelper, Attr attr) { // the displayName MUST be set String attrName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); EnumeratedStringPropertyDescriptor descriptor = new EnumeratedStringPropertyDescriptor(attrName, attrName, _getValidStrings(attrDecl, valuesHelper)); descriptor.setCategory(getCategory(attrDecl, attr)); descriptor.setDescription(attrName); if ((attrDecl.getUsage() != CMAttributeDeclaration.REQUIRED) && fSetExpertFilter) { descriptor.setFilterFlags(new String[]{IPropertySheetEntry.FILTER_ID_EXPERT}); } return descriptor; } /** * Creates a property descriptor for an attribute with a FIXED value - if * the value does not exist, an editable combo box is returned - if the * value exists but is not the fixed/default value, a combo box featuring * the current and correct value is returned - if the value exists and it * is the fixed/default value, no cell editor is provided "locking" the * value in */ private IPropertyDescriptor createFixedPropertyDescriptor(CMAttributeDeclaration attrDecl, CMDataType helper, Attr attr) { // the displayName MUST be set String attrName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); EnumeratedStringPropertyDescriptor descriptor = new EnumeratedStringPropertyDescriptor(attrName, attrName, _getValidFixedStrings(attrDecl, helper)); descriptor.setCategory(getCategory(attrDecl, attr)); descriptor.setDescription(DOMNamespaceHelper.computeName(attrDecl, fNode, null)); return descriptor; } protected IPropertyDescriptor createPropertyDescriptor(CMAttributeDeclaration attrDecl, Attr attr) { IPropertyDescriptor descriptor = null; CMDataType attrType = attrDecl.getAttrType(); if (attrType != null) { // handle declarations that provide FIXED/ENUMERATED values if ((attrType.getEnumeratedValues() != null) && (attrType.getEnumeratedValues().length > 0)) { descriptor = createEnumeratedPropertyDescriptor(attrDecl, attrType, attr); } else if (((attrDecl.getUsage() == CMAttributeDeclaration.FIXED) || (attrType.getImpliedValueKind() == CMDataType.IMPLIED_VALUE_FIXED)) && (attrType.getImpliedValue() != null)) { descriptor = createFixedPropertyDescriptor(attrDecl, attrType, attr); } else { // plain text descriptor = createTextPropertyDescriptor(attrDecl, attr); } } else { // no extra information given descriptor = createTextPropertyDescriptor(attrDecl, attr); } return descriptor; } /** * Returns the current collection of property descriptors. * * @return all valid descriptors. */ private IPropertyDescriptor[] createPropertyDescriptors() { CMNamedNodeMap attrMap = null; CMElementDeclaration ed = getDeclaration(); if (ed != null) { attrMap = ed.getAttributes(); CMNamedNodeMapImpl allAttributes = new CMNamedNodeMapImpl(attrMap); List nodes = ModelQueryUtil.getModelQuery(fNode.getOwnerDocument()).getAvailableContent((Element) fNode, ed, ModelQuery.INCLUDE_ATTRIBUTES); for (int k = 0; k < nodes.size(); k++) { CMNode cmnode = (CMNode) nodes.get(k); if (cmnode.getNodeType() == CMNode.ATTRIBUTE_DECLARATION) { allAttributes.put(cmnode); } } attrMap = allAttributes; } List descriptorList = new ArrayList(); List names = new ArrayList(); IPropertyDescriptor descriptor; CMAttributeDeclaration attrDecl = null; // add descriptors for existing attributes NamedNodeMap attributes = fNode.getAttributes(); if (attributes != null) { for (int i = 0; i < attributes.getLength(); i++) { Attr attr = (Attr) attributes.item(i); // if metainfo is present for this attribute, use the // CMAttributeDeclaration to derive a descriptor if (attrMap != null) { String attrName = attr.getName(); if (fCaseSensitive) { attrDecl = (CMAttributeDeclaration) attrMap.getNamedItem(attrName); } else { attrDecl = null; for (int j = 0; j < attrMap.getLength(); j++) { if (!fCaseSensitive && attrMap.item(j).getNodeName().equalsIgnoreCase(attrName)) { attrDecl = (CMAttributeDeclaration) attrMap.item(j); break; } } } } // be consistent: if there's metainfo, use *that* as the // descriptor ID descriptor = null; if (attrDecl != null) { String attrName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); if (!names.contains(attrName)) { descriptor = createPropertyDescriptor(attrDecl, attr); if (descriptor != null) names.add(attrName); } } else { if (!names.contains(attr.getName())) { descriptor = createDefaultPropertyDescriptor(attr.getName()); if (descriptor != null) names.add(attr.getName()); } } if (descriptor != null) { descriptorList.add(descriptor); } } } // add descriptors from the metainfo that are not yet listed if (attrMap != null) { for (int i = 0; i < attrMap.getLength(); i++) { attrDecl = (CMAttributeDeclaration) attrMap.item(i); String attrName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); if (!names.contains(attrName)) { IPropertyDescriptor holdDescriptor = createPropertyDescriptor(attrDecl, null); if (holdDescriptor != null) { names.add(attrName); descriptorList.add(holdDescriptor); } } } } // add MQE-based descriptors if (ed != null && fNode.getNodeType() == Node.ELEMENT_NODE) { List nodes = ModelQueryUtil.getModelQuery(fNode.getOwnerDocument()).getAvailableContent((Element) fNode, ed, ModelQuery.INCLUDE_ATTRIBUTES); for (int i = 0; i < nodes.size(); i++) { CMNode node = (CMNode) nodes.get(i); if (node.getNodeType() == CMNode.ATTRIBUTE_DECLARATION) { attrDecl = (CMAttributeDeclaration) node; String attrName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); if (!names.contains(attrName)) { IPropertyDescriptor holdDescriptor = createPropertyDescriptor(attrDecl, null); if (holdDescriptor != null) { names.add(attrName); descriptorList.add(holdDescriptor); } } } } } IPropertyDescriptor[] descriptors = new IPropertyDescriptor[descriptorList.size()]; for (int i = 0; i < descriptors.length; i++) { descriptors[i] = (IPropertyDescriptor) descriptorList.get(i); } return descriptors; } private IPropertyDescriptor createTextPropertyDescriptor(CMAttributeDeclaration attrDecl, Attr attr) { String attrName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); TextPropertyDescriptor descriptor = new TextPropertyDescriptor(attrName, attrName); descriptor.setCategory(getCategory(attrDecl, attr)); descriptor.setDescription(attrName); if ((attrDecl.getUsage() != CMAttributeDeclaration.REQUIRED) && fSetExpertFilter) { descriptor.setFilterFlags(new String[]{IPropertySheetEntry.FILTER_ID_EXPERT}); } return descriptor; } private String getCategory(CMAttributeDeclaration attrDecl, Attr attr) { if (attr != null) { String namespaceURI = attr.getNamespaceURI(); if (namespaceURI == null) namespaceURI = attr.getOwnerElement().getNamespaceURI(); if (namespaceURI != null) return namespaceURI; } if (attrDecl != null) { if (attrDecl.supports("category")) { //$NON-NLS-1$ return (String) attrDecl.getProperty("category"); //$NON-NLS-1$ } if (fShouldDeriveCategories && (attrDecl.getAttrType() != null) && (attrDecl.getAttrType().getDataTypeName() != null) && (attrDecl.getAttrType().getDataTypeName().length() > 0)) { return attrDecl.getAttrType().getDataTypeName(); } } return CATEGORY_ATTRIBUTES; } private CMElementDeclaration getDeclaration() { if ((fNode == null) || (fNode.getNodeType() != Node.ELEMENT_NODE)) { return null; } ModelQuery modelQuery = ModelQueryUtil.getModelQuery(fNode.getOwnerDocument()); if (modelQuery != null) { return modelQuery.getCMElementDeclaration((Element) fNode); } return null; } private Display getDisplay() { return PlatformUI.getWorkbench().getDisplay(); } /** * Returns a value for this Node that can be editted in a property sheet. * * @return a value that can be editted */ public Object getEditableValue() { return null; } /** * Returns the current collection of property descriptors. * * @return all valid descriptors. */ public final IPropertyDescriptor[] getPropertyDescriptors() { if ((fDescriptors == null) || (fDescriptors.length == 0)) { fDescriptors = createPropertyDescriptors(); } else { updatePropertyDescriptors(); } return fDescriptors; } /** * Returns the current value for the named property. * */ public Object getPropertyValue(Object nameObject) { String name = nameObject.toString(); String returnedValue = null; NamedNodeMap attrMap = fNode.getAttributes(); if (attrMap != null) { Node attribute = attrMap.getNamedItem(name); if (attribute != null) { if (attribute instanceof IDOMNode) { returnedValue = ((IDOMNode) attribute).getValueSource(); } else { returnedValue = attribute.getNodeValue(); } } } if (returnedValue == null) { returnedValue = ""; //$NON-NLS-1$ } return returnedValue; } private String[] getValidValues(CMAttributeDeclaration attrDecl) { if (attrDecl == null) { return new String[0]; } String[] validValues = null; CMDataType attrType = attrDecl.getAttrType(); if (attrType != null) { validValues = _getValidStrings(attrDecl, attrType); if (fSortEnumeratedValues) { Arrays.sort(validValues); } } if (validValues == null) { validValues = new String[0]; } return validValues; } public boolean isPropertyRemovable(Object id) { return true; } public boolean isPropertyResettable(Object id) { return fNode != null && fNode.getNodeType() == Node.ELEMENT_NODE; } /** * Returns whether the property value has changed from the default. * * @return <code>true</code> if the value of the specified property has * changed from its original default value; <code>false</code> * otherwise. */ public boolean isPropertySet(Object propertyObject) { String property = propertyObject.toString(); NamedNodeMap attrMap = fNode.getAttributes(); if (attrMap != null) { Node attr = attrMap.getNamedItem(property); return attr != null && (attr instanceof Attr ? ((Attr) attr).getSpecified() : true); } return false; } /** * Remove the given attribute from the Node * * @param propertyObject */ public void removeProperty(Object propertyObject) { NamedNodeMap attrMap = fNode.getAttributes(); if (attrMap != null) { Node attribute = attrMap.getNamedItem(propertyObject.toString()); if (attribute != null) { try { attrMap.removeNamedItem(propertyObject.toString()); } catch (DOMException e) { if (e.code != DOMException.INVALID_MODIFICATION_ERR) { Logger.logException(e); } } } } } /** * Resets the specified property's value to its default value. */ public void resetPropertyValue(Object propertyObject) { String property = propertyObject.toString(); CMNamedNodeMap attrDecls = null; CMElementDeclaration ed = getDeclaration(); if (ed != null) { attrDecls = ed.getAttributes(); CMNamedNodeMapImpl allAttributes = new CMNamedNodeMapImpl(attrDecls); List nodes = ModelQueryUtil.getModelQuery(fNode.getOwnerDocument()).getAvailableContent((Element) fNode, ed, ModelQuery.INCLUDE_ATTRIBUTES); for (int k = 0; k < nodes.size(); k++) { CMNode cmnode = (CMNode) nodes.get(k); if (cmnode.getNodeType() == CMNode.ATTRIBUTE_DECLARATION) { allAttributes.put(cmnode); } } attrDecls = allAttributes; } NamedNodeMap attrMap = fNode.getAttributes(); if (attrDecls != null) { CMAttributeDeclaration attrDecl = (CMAttributeDeclaration) attrDecls.getNamedItem(property); String defValue = null; if (attrDecl != null) { if (attrDecl.getAttrType() != null) { CMDataType helper = attrDecl.getAttrType(); if ((helper.getImpliedValueKind() != CMDataType.IMPLIED_VALUE_NONE) && (helper.getImpliedValue() != null)) { defValue = helper.getImpliedValue(); } } } if ((defValue != null) && (defValue.length() > 0)) { // implied values will be in the DOM, but not the source attrMap.removeNamedItem(property); } else { ((Attr) attrMap.getNamedItem(property)).setValue(""); //$NON-NLS-1$ } } else { // remember, this method is for reset, not remove ((Attr) attrMap.getNamedItem(property)).setValue(""); //$NON-NLS-1$ } } /** * Sets the named property to the given value. * */ public void setPropertyValue(Object nameObject, Object value) { // Avoid cycling - can happen if a closing cell editor causes a // refresh // on the PropertySheet page and the setInput again asks the editor to // close; besides, why apply the same value twice? if (!fValuesBeingSet.isEmpty() && (fValuesBeingSet.peek() == nameObject)) { return; } fValuesBeingSet.push(nameObject); String name = nameObject.toString(); String valueString = ""; //$NON-NLS-1$ if (value != null) { valueString = value.toString(); } NamedNodeMap attrMap = fNode.getAttributes(); try { if (attrMap != null) { Attr attr = (Attr) attrMap.getNamedItem(name); if (attr != null && attr.getSpecified()) { // EXISTING VALUE // potential out of control loop if updating the value // triggers a viewer update, forcing the // active cell editor to save its value and causing the // loop to continue if ((attr.getValue() == null) || !attr.getValue().equals(valueString)) { if (attr instanceof IDOMNode) { ((IDOMNode) attr).setValueSource(valueString); } else { attr.setValue(valueString); } } } else { // NEW(?) value Attr newAttr = fNode.getOwnerDocument().createAttribute(name); if (newAttr instanceof IDOMNode) { ((IDOMNode) newAttr).setValueSource(valueString); } else { newAttr.setValue(valueString); } attrMap.setNamedItem(newAttr); } } else { if (fNode instanceof Element) { ((Element) fNode).setAttribute(name, valueString); } } } catch (DOMException e) { Display d = getDisplay(); if (d != null) { d.beep(); } } fValuesBeingSet.pop(); } protected void updatePropertyDescriptors() { if ((fDescriptors == null) || (fDescriptors.length == 0)) { // Nothing to update return; } // List of all names encountered in the tag and defined by the element List declaredNames = new ArrayList(); // New descriptor list that will become fDescriptors after all // processing is done List descriptors = new ArrayList(); // Names of the descriptors in the above List List descriptorNames = new ArrayList(); // Update any descriptors derived from the metainfo CMElementDeclaration ed = getDeclaration(); CMNamedNodeMap attrMap = null; if (ed != null) { attrMap = ed.getAttributes(); CMNamedNodeMapImpl allAttributes = new CMNamedNodeMapImpl(attrMap); ModelQuery modelQuery = ModelQueryUtil.getModelQuery(fNode.getOwnerDocument()); if(modelQuery != null) { List nodes = modelQuery.getAvailableContent((Element) fNode, ed, ModelQuery.INCLUDE_ATTRIBUTES); for (int k = 0; k < nodes.size(); k++) { CMNode cmnode = (CMNode) nodes.get(k); if (cmnode.getNodeType() == CMNode.ATTRIBUTE_DECLARATION) { allAttributes.put(cmnode); } } } attrMap = allAttributes; } // Update exiting descriptors; not added to the final list here if (attrMap != null) { // Update existing descriptor types based on metainfo CMAttributeDeclaration attrDecl = null; for (int i = 0; i < attrMap.getLength(); i++) { attrDecl = (CMAttributeDeclaration) attrMap.item(i); String attrName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); if (!declaredNames.contains(attrName)) { declaredNames.add(attrName); } for (int j = 0; j < fDescriptors.length; j++) { boolean sameName = (fCaseSensitive && fDescriptors[j].getId().equals(attrDecl.getNodeName())) || (!fCaseSensitive && attrDecl.getNodeName().equals(fDescriptors[j].getId().toString())); if (sameName) { String[] validValues = getValidValues(attrDecl); // Update the descriptor for this // CMAttributeDeclaration (only enumerated values get // updated for now) if (fDescriptors[j] instanceof EnumeratedStringPropertyDescriptor) { ((EnumeratedStringPropertyDescriptor) fDescriptors[j]).updateValues(validValues); } // Replace with better descriptor else if ((validValues != null) && (validValues.length > 0)) { fDescriptors[j] = createPropertyDescriptor(attrDecl, null); } } } } } else { // Update existing descriptors based on not having any metainfo for (int j = 0; j < fDescriptors.length; j++) { // Replace with basic descriptor if (!(fDescriptors[j] instanceof TextPropertyDescriptor)) { fDescriptors[j] = createDefaultPropertyDescriptor((String) fDescriptors[j].getId()); } } } NamedNodeMap attributes = fNode.getAttributes(); // Remove descriptors for attributes that aren't present AND aren't // known through metainfo, // do this by only reusing existing descriptors for attributes that // are present or declared for (int i = 0; i < fDescriptors.length; i++) { if (fDescriptors[i] != null) { String descriptorName = fDescriptors[i].getId().toString(); if ((declaredNames.contains(descriptorName) || (attributes.getNamedItem(descriptorName) != null)) && !descriptorNames.contains(descriptorName)) { descriptorNames.add(descriptorName); descriptors.add(fDescriptors[i]); } } } // Add descriptors for declared attributes that don't already have one if (attrMap != null) { // Update existing descriptor types based on metainfo CMAttributeDeclaration attrDecl = null; for (int i = 0; i < attrMap.getLength(); i++) { attrDecl = (CMAttributeDeclaration) attrMap.item(i); String attrName = DOMNamespaceHelper.computeName(attrDecl, fNode, null); if (fCaseSensitive) { if (!descriptorNames.contains(attrName)) { IPropertyDescriptor descriptor = createPropertyDescriptor(attrDecl, null); if (descriptor != null) { descriptorNames.add(attrName); descriptors.add(descriptor); } } } else { boolean exists = false; for (int j = 0; j < descriptorNames.size(); j++) { exists = (descriptorNames.get(j).toString().equalsIgnoreCase(attrName)) || exists; } if (!exists) { descriptorNames.add(attrName); IPropertyDescriptor descriptor = createPropertyDescriptor(attrDecl, null); if (descriptor != null) { descriptorNames.add(attrName); descriptors.add(descriptor); } } } } } // Add descriptors for existing attributes that don't already have one if (attributes != null) { for (int i = 0; i < attributes.getLength(); i++) { Attr attr = (Attr) attributes.item(i); String attrName = attr.getName(); if (fCaseSensitive) { if (!descriptorNames.contains(attrName)) { descriptorNames.add(attrName); descriptors.add(createDefaultPropertyDescriptor(attrName)); } } else { boolean exists = false; for (int j = 0; j < descriptorNames.size(); j++) { exists = (descriptorNames.get(j).toString().equalsIgnoreCase(attrName)) || exists; } if (!exists) { descriptorNames.add(attrName); descriptors.add(createDefaultPropertyDescriptor(attrName)); } } } } // Update fDescriptors IPropertyDescriptor[] newDescriptors = new IPropertyDescriptor[descriptors.size()]; for (int i = 0; i < newDescriptors.length; i++) { newDescriptors[i] = (IPropertyDescriptor) descriptors.get(i); } fDescriptors = newDescriptors; } }