/*******************************************************************************
* Copyright (c) 2009 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
*******************************************************************************/
package org.eclipse.jst.jsp.core.internal.modelquery;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.jst.jsp.core.internal.contentmodel.JSPCMDocumentFactory;
import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache;
import org.eclipse.jst.jsp.core.internal.provisional.JSP12Namespace;
import org.eclipse.jst.jsp.core.internal.provisional.JSP20Namespace;
import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.internal.Workbench;
import org.eclipse.wst.html.core.internal.contentmodel.JSPCMDocument;
import org.eclipse.wst.html.core.internal.contentmodel.TapestryElementCollection.ElemDecl;
import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument;
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.modelquery.ModelQuery;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.extension.ModelQueryExtension;
import org.eclipse.wst.xml.core.internal.provisional.contentmodel.CMDocType;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.ssemodelquery.ModelQueryAdapter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* An implementation of {@link ModelQueryExtension} for JSP and Tapestry tags in JSP documents
*
* @author gavingui2011@gmail.com - Beijing China
*
* ������ļ�����tapestry��auto complete���ܵ�
*/
public class JSPModelQueryExtension extends ModelQueryExtension {
private static final String TAG_JSP_ROOT = "jsp:root";
private static List<TapestryComponentModel> tapestryCustomComponents = null;
/**
* Originally taken from JSPContentAssistProcessor
*
* @see org.eclipse.wst.xml.core.internal.contentmodel.modelquery.extension.ModelQueryExtension#getAvailableElementContent(org.w3c.dom.Element, java.lang.String, int)
*/
public CMNode[] getAvailableElementContent(Element parentElement,
String namespace, int includeOptions) {
CMNode[] nodes = EMPTY_CMNODE_ARRAY;
ArrayList nodeList = new ArrayList();
//only returns anything if looking for child nodes
if(((includeOptions & ModelQuery.INCLUDE_CHILD_NODES) != 0) && parentElement instanceof IDOMNode) {
IDOMNode node = (IDOMNode)parentElement;
// get position dependent CMDocuments and insert their tags as
// proposals
ModelQueryAdapter mqAdapter = null;
if (node.getNodeType() == Node.DOCUMENT_NODE) {
mqAdapter = (ModelQueryAdapter) node.getAdapterFor(ModelQueryAdapter.class);
} else {
mqAdapter = (ModelQueryAdapter) ((IDOMNode) node.getOwnerDocument()).getAdapterFor(ModelQueryAdapter.class);
}
if (mqAdapter != null) {
CMDocument doc = mqAdapter.getModelQuery().getCorrespondingCMDocument(node);
if (doc != null) {
CMDocument jcmdoc = getDefaultJSPCMDocument(node);
CMNamedNodeMap jspelements = jcmdoc.getElements();
/* For a built-in JSP action the content model is properly
* set up, so don't just blindly add the rest--unless this
* will be a direct child of the document
*/
if (jspelements != null && (!(doc instanceof JSPCMDocument) || node.getNodeType() == Node.DOCUMENT_NODE)) {
List rejectElements = new ArrayList();
// determine if the document is in XML form
Document domDoc = null;
if (node.getNodeType() == Node.DOCUMENT_NODE) {
domDoc = (Document) node;
} else {
domDoc = node.getOwnerDocument();
}
// Show XML tag forms of JSP markers if jsp:root is
// the document element OR it's HTML but
// isn't really in the text.
// If the document isn't strictly XML, pull out the
// XML tag forms it is xml format
rejectElements.add(JSP12Namespace.ElementName.SCRIPTLET);
rejectElements.add(JSP12Namespace.ElementName.EXPRESSION);
rejectElements.add(JSP12Namespace.ElementName.DECLARATION);
rejectElements.add(JSP12Namespace.ElementName.DIRECTIVE_INCLUDE);
rejectElements.add(JSP12Namespace.ElementName.DIRECTIVE_PAGE);
rejectElements.add(JSP12Namespace.ElementName.TEXT);
rejectElements.add(JSP12Namespace.ElementName.DIRECTIVE_TAGLIB);
rejectElements.add(JSP20Namespace.ElementName.DIRECTIVE_TAG);
rejectElements.add(JSP20Namespace.ElementName.DIRECTIVE_ATTRIBUTE);
rejectElements.add(JSP20Namespace.ElementName.DIRECTIVE_VARIABLE);
if (isXMLFormat(domDoc)) {
// jsp actions
rejectElements.add(JSP12Namespace.ElementName.FALLBACK);
rejectElements.add(JSP12Namespace.ElementName.USEBEAN);
rejectElements.add(JSP12Namespace.ElementName.GETPROPERTY);
rejectElements.add(JSP12Namespace.ElementName.SETPROPERTY);
rejectElements.add(JSP12Namespace.ElementName.INCLUDE);
rejectElements.add(JSP12Namespace.ElementName.FORWARD);
rejectElements.add(JSP12Namespace.ElementName.PLUGIN);
rejectElements.add(JSP12Namespace.ElementName.FALLBACK);
rejectElements.add(JSP12Namespace.ElementName.PARAM);
rejectElements.add(JSP12Namespace.ElementName.PARAMS);
}
// don't show jsp:root if a document element already
// exists
Element docElement = domDoc.getDocumentElement();
if (docElement != null &&((docElement.getNodeName().equals(TAG_JSP_ROOT)) ||
((((IDOMNode) docElement).getStartStructuredDocumentRegion() != null ||
((IDOMNode) docElement).getEndStructuredDocumentRegion() != null)))) {
rejectElements.add(JSP12Namespace.ElementName.ROOT);
}
//Add JSP elements into autocomplete menu
for (int j = 0; j < jspelements.getLength(); j++) {
CMElementDeclaration ed = (CMElementDeclaration) jspelements.item(j);
if (!rejectElements.contains(ed.getNodeName())) {
nodeList.add(ed);
}
}
//Add Tapestry components into autocomplete menu
ElemDecl temp = null;
jcmdoc = getDefaultTapestryCMDocument();
CMNamedNodeMap tapestryelements = jcmdoc.getElements();
for (int j = 0; j < tapestryelements.getLength(); j++) {
if(j == 0) temp = (ElemDecl) tapestryelements.item(j);
CMElementDeclaration ed = (CMElementDeclaration) tapestryelements.item(j);
if (!rejectElements.contains(ed.getNodeName())) {
nodeList.add(ed);
}
}
//load tapestry 5 custom components
if(temp != null){
collectCustomComponents();
if(tapestryCustomComponents != null){
for(int i=0; i<tapestryCustomComponents.size(); i++){
TapestryComponentModel tcm = tapestryCustomComponents.get(i);
ElemDecl cloneElement = (ElemDecl) temp.clone(tcm.nodeName, tcm.attributes);
nodeList.add(cloneElement);
}
}
}
}
}
// No cm document (such as for the Document (a non-Element) node itself)
else {
CMNamedNodeMap jspElements = getDefaultJSPCMDocument(node).getElements();
int length = jspElements.getLength();
for (int i = 0; i < length; i++) {
nodeList.add(jspElements.item(i));
}
}
}
nodes = (CMNode[])nodeList.toArray(new CMNode[nodeList.size()]);
}
return nodes;
}
private void collectCustomComponents(){
IEditorPart editorPart = Workbench.getInstance()
.getActiveWorkbenchWindow().getActivePage().getActiveEditor();
if (editorPart != null) {
IFileEditorInput input = (IFileEditorInput) editorPart
.getEditorInput();
IFile file = input.getFile();
IProject project = file.getProject();
final IFile res = project.getFile("/components.tcc");
if(res.exists()){
List<TapestryComponentModel> nodeList = new ArrayList<TapestryComponentModel>();
loadTapestryCustomComponentsTags(res, nodeList);
tapestryCustomComponents = nodeList;
}
}
}
private void loadTapestryCustomComponentsTags(final IFile componentsFile, List<TapestryComponentModel> nodeList){
final DocumentBuilderFactory domfac = DocumentBuilderFactory
.newInstance();
DocumentBuilder dombuilder = null;
try {
dombuilder = domfac.newDocumentBuilder();
Document domDoc = dombuilder.parse(componentsFile.getContents());
Element root = domDoc.getDocumentElement();
NodeList components = root.getChildNodes();
if (components != null) {
for (int i = 0; i < components.getLength(); i++) {
Node component = components.item(i);
if(component.getNodeType() == Node.ELEMENT_NODE && component.getNodeName().trim().equals("components")){
loadCustomComponents(component, nodeList);
break;
}
}
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CoreException e) {
e.printStackTrace();
}
}
private void loadCustomComponents(Node root, List<TapestryComponentModel> nodeList){
NodeList components = root.getChildNodes();
if (components != null) {
for (int i = 0; i < components.getLength(); i++) {
Node component = components.item(i);
if(component.getNodeType() == Node.ELEMENT_NODE && component.getNodeName().trim().equals("component")){
Node name = component.getAttributes().getNamedItem("name");
if (name != null){
TapestryComponentModel tcm = new TapestryComponentModel();
tcm.nodeName = name.getNodeValue();
Node attributes = component.getAttributes().getNamedItem("attributes");
if(attributes != null){
tcm.attributes = attributes.getNodeValue().trim().split(",");
}
nodeList.add(tcm);
}
}
}
}
}
private CMElementDeclaration CMElementDeclaration(String prefix, String name, Map<String,String> attributes){
return null;
}
/**
* <p>For JSP files and segments, this is just the JSP
* document, but when editing tag files and their fragments, it
* should be the tag document.</p>
*
* <p>It may also vary based on the model being edited in the future.</p>
*
* <p><b>NOTE:</b>Copied from JSPContentAssistProcessor</p>
*
* @return the default non-embedded CMDocument for the document being
* edited.
*/
private CMDocument getDefaultJSPCMDocument(IDOMNode node) {
CMDocument jcmdoc = null;
// handle tag files here
String contentType = node.getModel().getContentTypeIdentifier();
if (ContentTypeIdForJSP.ContentTypeID_JSPTAG.equals(contentType)) {
jcmdoc = JSPCMDocumentFactory.getCMDocument(CMDocType.TAG20_DOC_TYPE);
} else {
String modelPath = node.getModel().getBaseLocation();
if (modelPath != null && !IModelManager.UNMANAGED_MODEL.equals(modelPath)) {
float version = DeploymentDescriptorPropertyCache.getInstance().getJSPVersion(new Path(modelPath));
jcmdoc = JSPCMDocumentFactory.getCMDocument(version);
}
if (jcmdoc == null) {
jcmdoc = JSPCMDocumentFactory.getCMDocument();
}
}
return jcmdoc;
}
private CMDocument getDefaultTapestryCMDocument() {
CMDocument jcmdoc = JSPCMDocumentFactory.getTapestryCMDocument();
return jcmdoc;
}
/**
* <p><b>NOTE:</b>Copied from JSPContentAssistProcessor</p>
*
* @param doc determine if this {@link Document} is in an XML format
* @return is the given document in an XML format
*/
private boolean isXMLFormat(Document doc) {
boolean result = false;
if (doc != null) {
Element docElement = doc.getDocumentElement();
result = docElement != null &&
((docElement.getNodeName().equals(TAG_JSP_ROOT)) ||
((((IDOMNode) docElement).getStartStructuredDocumentRegion() == null &&
((IDOMNode) docElement).getEndStructuredDocumentRegion() == null)));
}
return result;
}
}
class TapestryComponentModel{
public String nodeName;
public String[] attributes;
}