blob: f18d7c88df9d09c6a0347d696d1db00e1abd1e8b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2010 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.contentassist;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.contentassist.ContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration;
import org.eclipse.wst.xml.core.internal.contentmodel.CMContent;
import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
import org.eclipse.wst.xml.core.internal.contentmodel.CMGroup;
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.CMNodeList;
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.modelquery.ModelQueryUtil;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* Calculates attribute context information based on a StructuedDocument and
* document position.
*
* @author pavery
*/
public class AttributeContextInformationProvider {
private final IContextInformation[] EMPTY_CONTEXT_INFO = new IContextInformation[0];
private Comparator fComparator;
private IStructuredDocument fDocument = null;
private ContextInfoModelUtil fModelUtil = null;
public AttributeContextInformationProvider(IStructuredDocument doc, AttributeContextInformationPresenter presenter) {
fDocument = doc;
fModelUtil = new ContextInfoModelUtil(fDocument);
}
/**
* @param sdRegion
*
*/
private boolean canProposeInfo(IStructuredDocumentRegion sdRegion) {
if ((sdRegion != null) && isEndTag(sdRegion)) {
return false;
}
else {
return true;
}
}
public IContextInformation[] getAttributeInformation(int offset) {
/*
* need to take care of special cases w/ ambiguous regions <tag>|
* </tag> also end tags..
*/
IContextInformation[] results = EMPTY_CONTEXT_INFO;
IStructuredDocumentRegion sdRegion = fModelUtil.getDocument().getRegionAtCharacterOffset(offset);
if (!canProposeInfo(sdRegion)) {
return EMPTY_CONTEXT_INFO;
}
IDOMNode node = fModelUtil.getXMLNode(offset);
if (node != null) {
switch (node.getNodeType()) {
case Node.ELEMENT_NODE :
results = getInfoForElement(node);
break;
// future...
// case Node.TEXT_NODE :
// results = getInfoForText(node);
// break;
}
}
return results;
}
/**
* Returns a comparator that compares CMAttributeDeclaration names.
*
* the comparator
*/
private Comparator getCMAttributeComparator() {
if (fComparator == null) {
fComparator = new Comparator() {
public int compare(Object o1, Object o2) {
return ((CMAttributeDeclaration) o1).getAttrName().compareTo(((CMAttributeDeclaration) o2).getAttrName());
}
};
}
return fComparator;
}
/**
* @param node
*
*/
private IContextInformation[] getInfoForElement(IDOMNode node) {
IContextInformation[] results = EMPTY_CONTEXT_INFO;
CMElementDeclaration decl = fModelUtil.getModelQuery().getCMElementDeclaration((Element) node);
if (decl != null) {
CMNamedNodeMap attributes = decl.getAttributes();
CMNamedNodeMapImpl allAttributes = new CMNamedNodeMapImpl(attributes);
List nodes = ModelQueryUtil.getModelQuery(node.getOwnerDocument()).getAvailableContent((Element) node, decl, 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);
}
}
attributes = allAttributes;
String attrContextString = node.getNodeName();
StringBuffer attrInfo = new StringBuffer(" "); //$NON-NLS-1$
String name = ""; //$NON-NLS-1$
HashMap attrPosMap = new HashMap();
int pos = 0;
int length = 0;
int numPerLine = 8;
CMAttributeDeclaration[] sortedAttrs = getSortedAttributes(attributes);
for (int i = 0; i < sortedAttrs.length; i++) {
name = sortedAttrs[i].getAttrName();
length = name.length();
pos = attrInfo.length();
attrInfo.append(name);
if (sortedAttrs[i].getUsage() == CMAttributeDeclaration.REQUIRED) {
attrInfo.append("*"); //$NON-NLS-1$
length++;
}
if (i < attributes.getLength() - 1) {
attrInfo.append(" "); //$NON-NLS-1$
if ((i != 0) && (i % numPerLine == 0)) {
attrInfo.append("\n "); //$NON-NLS-1$
}
}
attrPosMap.put(name, new Position(pos, length));
}
if (!attrInfo.toString().trim().equals("")) {
return new IContextInformation[]{new AttributeContextInformation(attrContextString, attrInfo.toString(), attrPosMap)};
}
}
return results;
}
/**
* @param node
*
*/
IContextInformation[] getInfoForText(IDOMNode node) {
Node parent = node.getParentNode();
String contextString = node.getNodeName();
StringBuffer info = new StringBuffer(" "); //$NON-NLS-1$
if ((parent != null) && (parent.getNodeType() == Node.ELEMENT_NODE)) {
CMElementDeclaration decl = fModelUtil.getModelQuery().getCMElementDeclaration((Element) parent);
CMContent content = decl.getContent();
if (content instanceof CMGroup) {
CMGroup cmGroup = (CMGroup) content;
CMNodeList children = cmGroup.getChildNodes();
CMNode cmNode = null;
for (int i = 0; i < children.getLength(); i++) {
cmNode = children.item(i);
contextString = cmNode.getNodeName();
if (contextString != null) {
info.append("<" + cmNode.getNodeName() + ">"); //$NON-NLS-1$ //$NON-NLS-2$
if (i < children.getLength() - 1) {
info.append(" "); //$NON-NLS-1$
}
}
}
}
}
if (!info.toString().trim().equals("")) {
return new IContextInformation[]{new ContextInformation(contextString, info.toString())};
}
else {
return EMPTY_CONTEXT_INFO;
}
}
/**
* Returns sorted array of CMAttributeDeclarations.
*
* @param attributes
*
*/
private CMAttributeDeclaration[] getSortedAttributes(CMNamedNodeMap attributes) {
List sorted = new ArrayList();
for (int i = 0; i < attributes.getLength(); i++) {
sorted.add(attributes.item(i));
}
Collections.sort(sorted, getCMAttributeComparator());
return (CMAttributeDeclaration[]) sorted.toArray(new CMAttributeDeclaration[sorted.size()]);
}
/**
* @param sdRegion
*
*/
private boolean isEndTag(IStructuredDocumentRegion sdRegion) {
ITextRegionList regions = sdRegion.getRegions();
return regions.get(0).getType() == DOMRegionContext.XML_END_TAG_OPEN;
}
}