blob: 1033d09f3fcd3ef36d6b7e4e6d05d6ef99150108 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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
*******************************************************************************/
package org.eclipse.wst.html.core.internal.document;
import org.eclipse.wst.html.core.internal.contentmodel.HTMLElementDeclaration;
import org.eclipse.wst.html.core.internal.provisional.HTML40Namespace;
import org.eclipse.wst.html.core.internal.provisional.HTMLCMProperties;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
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.CMNode;
import org.eclipse.wst.xml.core.internal.contentmodel.CMNodeList;
import org.eclipse.wst.xml.core.internal.document.CMNodeUtil;
import org.eclipse.wst.xml.core.internal.document.ModelParserAdapter;
import org.eclipse.wst.xml.core.internal.document.TagAdapter;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMText;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* HTMLDocumentImpl class
*/
public class HTMLModelParserAdapter implements ModelParserAdapter {
/**
* note: I made public, temparily, so could be used by JSPLoader
*/
protected HTMLModelParserAdapter() {
super();
}
private boolean shouldTerminateAt(CMElementDeclaration parent, CMElementDeclaration child) {
if (!parent.supports(HTMLCMProperties.TERMINATORS))
return false;
java.util.Iterator i = (java.util.Iterator) parent.getProperty(HTMLCMProperties.TERMINATORS);
if (i == null)
return false;
String nextName = child.getElementName();
while (i.hasNext()) {
// NOTE: CMElementDeclaration of child is not always HTMLCMElementDeclaration.
// It might be one of based on DTD (for XHTML element). So, comparison must
// be performed ignoring case.
// -- 3/20/2002
String terminator = (String) i.next();
if (terminator == null)
continue;
if (nextName.equalsIgnoreCase(terminator))
return true;
}
return false;
}
/**
*/
public boolean canContain(Element element, Node child) {
if (element == null || child == null)
return false;
IDOMElement impl = (IDOMElement) element;
if (child.getNodeType() == Node.ELEMENT_NODE) {
if (!impl.isGlobalTag())
return true; // non HTML tag
IDOMElement childElement = (IDOMElement) child;
CMElementDeclaration myDec = CMNodeUtil.getElementDeclaration(element);
if (myDec == null)
return true;
//if (!(myDec instanceof HTMLElementDeclaration)) return true;
if (myDec.getContentType() == CMElementDeclaration.EMPTY)
return false;
if (!childElement.isGlobalTag())
return true; // non HTML tag
CMElementDeclaration childDec = CMNodeUtil.getElementDeclaration(childElement);
if (childDec == null)
return true;
//if (!(childDec instanceof HTMLElementDeclaration)) return true;
if (myDec instanceof HTMLElementDeclaration) {
if (((Boolean) ((HTMLElementDeclaration) myDec).getProperty(HTMLCMProperties.IS_JSP)).booleanValue())
return true;
}
if (shouldTerminateAt(myDec, childDec) && !isValidChild(myDec, childDec)) {
return false;
}
String tagName = impl.getTagName();
if (tagName == null)
return true;
String childName = childElement.getTagName();
if (childName == null)
return true;
if (!impl.hasStartTag() && !impl.hasEndTag()) {
// implicit element
if (tagName.equalsIgnoreCase(childElement.getTagName()))
return false;
if (tagName.equalsIgnoreCase(HTML40Namespace.ElementName.HEAD)) {
if (!childName.equalsIgnoreCase(HTML40Namespace.ElementName.META) && !childName.equalsIgnoreCase(HTML40Namespace.ElementName.TITLE) && !childName.equalsIgnoreCase(HTML40Namespace.ElementName.LINK) && !childName.equalsIgnoreCase(HTML40Namespace.ElementName.STYLE) && !childName.equalsIgnoreCase(HTML40Namespace.ElementName.BASE) && !childName.equalsIgnoreCase(HTML40Namespace.ElementName.ISINDEX)) {
return false;
}
}
Node parent = element.getParentNode();
if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) {
IDOMElement parentElement = (IDOMElement) parent;
if (!parentElement.hasStartTag() && !parentElement.hasEndTag()) {
if (!canContain(parentElement, child))
return false;
}
}
return true;
}
// contexual termination for TABLE content tags
boolean isTableContent = false;
if (childName.equalsIgnoreCase(HTML40Namespace.ElementName.TBODY) || childName.equalsIgnoreCase(HTML40Namespace.ElementName.THEAD) || childName.equalsIgnoreCase(HTML40Namespace.ElementName.TFOOT)) {
if (tagName.equalsIgnoreCase(HTML40Namespace.ElementName.TABLE))
return true;
isTableContent = true;
}
else if (childName.equalsIgnoreCase(HTML40Namespace.ElementName.TR)) {
if (tagName.equalsIgnoreCase(HTML40Namespace.ElementName.TBODY) || tagName.equalsIgnoreCase(HTML40Namespace.ElementName.THEAD) || tagName.equalsIgnoreCase(HTML40Namespace.ElementName.TFOOT) || tagName.equalsIgnoreCase(HTML40Namespace.ElementName.TABLE))
return true;
isTableContent = true;
}
else if (childName.equalsIgnoreCase(HTML40Namespace.ElementName.TD) || childName.equalsIgnoreCase(HTML40Namespace.ElementName.TH)) {
if (tagName.equalsIgnoreCase(HTML40Namespace.ElementName.TR) || tagName.equalsIgnoreCase(HTML40Namespace.ElementName.TBODY) || tagName.equalsIgnoreCase(HTML40Namespace.ElementName.THEAD) || tagName.equalsIgnoreCase(HTML40Namespace.ElementName.TFOOT) || tagName.equalsIgnoreCase(HTML40Namespace.ElementName.TABLE))
return true;
isTableContent = true;
}
if (isTableContent) {
// TABLE content tags should terminate non TABLE content tags,
// if in TABLE
for (Node parent = element.getParentNode(); parent != null; parent = parent.getParentNode()) {
if (parent.getNodeType() != Node.ELEMENT_NODE)
break;
IDOMElement parentElement = (IDOMElement) parent;
String parentName = parentElement.getTagName();
if (parentName == null)
continue;
if (parentName.equalsIgnoreCase(HTML40Namespace.ElementName.TABLE))
return false;
}
}
if (tagName.equalsIgnoreCase(HTML40Namespace.ElementName.EMBED)) {
if (!childName.equalsIgnoreCase(HTML40Namespace.ElementName.NOEMBED))
return false;
}
}
else if (child.getNodeType() == Node.TEXT_NODE) {
String tagName = impl.getTagName();
if (tagName != null && tagName.equalsIgnoreCase(HTML40Namespace.ElementName.EMBED)) {
IDOMText text = (IDOMText) child;
if (!text.isElementContentWhitespace())
return false;
}
}
else if (child.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
if (impl.isImplicitTag())
return false;
}
return true;
}
/**
*/
public boolean canBeImplicitTag(Element element) {
return false;
}
/**
*/
public boolean canBeImplicitTag(Element element, Node child) {
return false;
}
/**
*/
public Element createCommentElement(Document document, String data, boolean isJSPTag) {
if (document == null || data == null || data.length() == 0)
return null;
return createMetaElement(document, data, isJSPTag);
}
/**
* This routine create an implicit Element for given parent and child,
* such as HTML, BODY, HEAD, and TBODY for HTML document.
*/
public Element createImplicitElement(Document document, Node parent, Node child) {
return null;
}
/**
*/
private Element createMetaElement(Document document, String data, boolean isJSPTag) {
if (data == null || data.length() == 0)
return null;
TagScanner scanner = new TagScanner(data, 0, true); // one line
String name = scanner.nextName();
if (name == null || !name.equalsIgnoreCase(MetaData.METADATA))
return null;
String type = null;
boolean isStartSpan = false;
boolean isEndSpan = false;
name = scanner.nextName();
while (name != null) {
String value = scanner.nextValue();
if (name.equalsIgnoreCase(MetaData.TYPE)) {
if (value == null)
return null;
if (value.equalsIgnoreCase(MetaData.DESIGNER_CONTROL)) {
type = MetaData.DESIGNER_CONTROL;
}
else if (value.equalsIgnoreCase(MetaData.DYNAMIC_DATA)) {
type = MetaData.DYNAMIC_DATA;
}
else if (value.equalsIgnoreCase(MetaData.AUTHOR_TIME_VISUAL)) {
type = MetaData.AUTHOR_TIME_VISUAL;
}
else if (value.equalsIgnoreCase(MetaData.ANNOTATION)) {
type = MetaData.ANNOTATION;
}
else {
return null;
}
}
else if (name.equalsIgnoreCase(MetaData.STARTSPAN)) {
isStartSpan = true;
}
else if (name.equalsIgnoreCase(MetaData.ENDSPAN)) {
if (!isStartSpan)
isEndSpan = true;
}
name = scanner.nextName();
}
if (type == null)
return null;
if (!isStartSpan && !isEndSpan)
return null;
String metaData = null;
int offset = scanner.getNextOffset(); // skip new line
if (offset < data.length())
metaData = data.substring(offset);
if (metaData == null)
metaData = new String();
IDOMElement element = (IDOMElement) document.createElement(MetaData.PREFIX + type);
MetaDataAdapter adapter = new MetaDataAdapter(type);
if (isStartSpan) {
if (metaData != null)
adapter.setData(metaData);
}
else {
if (metaData != null)
adapter.setEndData(metaData);
}
element.addAdapter(adapter);
adapter.setElement(element);
element.setJSPTag(isJSPTag);
return element;
}
/**
*/
public String getFindRootName(String tagName) {
if (tagName == null)
return null;
// tag matching should not beyond TABLE tag except BODY tag
if (tagName.equalsIgnoreCase(HTML40Namespace.ElementName.BODY))
return null;
return HTML40Namespace.ElementName.TABLE;
}
/**
*/
public boolean isAdapterForType(Object type) {
return (type == ModelParserAdapter.class);
}
/**
*/
public boolean isEndTag(IDOMElement element) {
TagAdapter adapter = (TagAdapter) element.getExistingAdapter(TagAdapter.class);
if (adapter != null)
return adapter.isEndTag();
return element.isEndTag();
}
/**
*/
public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) {
// do nothing on notifiy change
// TODO: this means good candidate for regular platform adapter
}
private static boolean isValidChild(CMElementDeclaration parent, CMElementDeclaration child) {
if (parent == null || child == null)
return false;
CMContent content = parent.getContent();
if (content == null)
return false;
return isChild(content, child);
}
/**
*/
private static boolean isChild(CMContent content, CMElementDeclaration target) {
switch (content.getNodeType()) {
case CMNode.ELEMENT_DECLARATION :
return isSameDeclaration((CMElementDeclaration) content, target);
case CMNode.GROUP :
CMNodeList children = ((CMGroup) content).getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
CMNode child = children.item(i);
switch (child.getNodeType()) {
case CMNode.ELEMENT_DECLARATION :
if (isSameDeclaration((CMElementDeclaration) child, target))
return true;
continue; // Go next child.
case CMNode.GROUP :
if (isChild((CMContent) child, target))
return true;
continue; // Go next child.
default :
continue; // Go next child.
}
}
}
return false;
}
/**
*/
private static boolean isSameDeclaration(CMElementDeclaration aDec, CMElementDeclaration otherDec) {
return aDec.getElementName() == otherDec.getElementName();
}
}