blob: 8634ec4f8b7c974fb9d57dbb4792e138eab6af6c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2004 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.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
*/
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();
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("")) //$NON-NLS-1$
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("")) //$NON-NLS-1$
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;
}
}