blob: f7e3772430c2ecadedc891764ac13c24c4586cba [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2005 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.ui.internal.breakpointproviders;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jst.jsp.core.internal.contentmodel.tld.provisional.TLDElementDeclaration;
import org.eclipse.jst.jsp.core.internal.provisional.JSP12Namespace;
import org.eclipse.jst.jsp.core.internal.regions.DOMJSPRegionContexts;
import org.eclipse.ui.IEditorInput;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
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.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionCollection;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.sse.ui.internal.provisional.extensions.ISourceEditingTextTools;
import org.eclipse.wst.sse.ui.internal.provisional.extensions.breakpoint.IBreakpointProvider;
import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
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.modelquery.ModelQueryUtil;
import org.eclipse.wst.xml.core.internal.provisional.contentmodel.CMNodeWrapper;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.eclipse.wst.xml.ui.internal.provisional.IDOMSourceEditingTextTools;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Abstract breakpoint provider class which implements breakpoint provider
* interface.
*
* This is a temporary class for JavaBreakpointProvider and
* JavaScriptBreakpointProvider, and should be refactored to separate Java and
* JavaScript parts.
*/
public abstract class AbstractBreakpointProvider implements IBreakpointProvider {
protected static final int END_OF_LINE = -1;
protected static final int JAVA = 1;
protected static final int JAVASCRIPT = 2;
private static final String[] JAVASCRIPT_LANGUAGE_KEYS = new String[]{"javascript", "javascript1.0", "javascript1.1_3", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
"javascript1.2", "javascript1.3", "javascript1.4", "javascript1.5", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
"javascript1.6", "jscript", "sashscript"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
private static final String JSP_DIRECTIVE_PAGE = "jsp:directive.page"; //$NON-NLS-1$
protected static final int NO_VALID_CONTENT = -2;
protected static final int UNSUPPORTED = 0;
protected static boolean contains(String[] haystack, String needle) {
for (int i = 0; i < haystack.length; i++) {
if (haystack[i].equals(needle)) {
return true;
}
}
return false;
}
/*
* Return the page language
*/
protected static int getPageLanguage(Document doc) {
if (doc == null)
return UNSUPPORTED;
NodeList pageDirectives = doc.getElementsByTagName(JSP_DIRECTIVE_PAGE);
// Search for first language directive
for (int i = 0; i < pageDirectives.getLength(); i++) {
Node child = pageDirectives.item(i);
Node languageAttr = child.getAttributes().getNamedItem("language"); //$NON-NLS-1$
if (languageAttr != null) {
String pageLanguage = languageAttr.getNodeValue();
if (pageLanguage == null || pageLanguage.length() == 0)
return UNSUPPORTED;
pageLanguage = pageLanguage.toLowerCase();
if (contains(JAVASCRIPT_LANGUAGE_KEYS, pageLanguage))
return JAVASCRIPT;
else if (pageLanguage.equals("java"))//$NON-NLS-1$
return JAVA;
else
return UNSUPPORTED;
}
}
return JAVA; // Java is default if no language directive
}
/*
* Search the RegionContainer's regions looking for JSP content. If valid
* content is found, return the position >= 0 If no valid content is
* found, return NO_VALID_CONTENT. If a region starts after the line's
* endOffset, return END_OF_LINE.
*/
private static int getValidRegionPosition(IStructuredModel model, ITextRegionCollection regionContainer, int startOffset, int endOffset) {
ITextRegionList regions = regionContainer.getRegions();
for (int i = 0; i < regions.size(); i++) {
ITextRegion region = regions.get(i);
if (region instanceof ITextRegionCollection) {
int validPosition = getValidRegionPosition(model, (ITextRegionCollection) region, startOffset, endOffset);
if (validPosition == END_OF_LINE || validPosition >= 0)
return validPosition;
}
else {
// region must be at least partially on selected line
if (regionContainer.getEndOffset(region) > startOffset) {
int regionStartOffset = regionContainer.getStartOffset(region);
// if region starts after line's endOffset, we're done
// searching
if (regionStartOffset > endOffset)
return END_OF_LINE;
// If region is JSP content, make sure the language is
// Java not Javascript by
// checking the content assist adapter's type.
if (region.getType().equals(DOMJSPRegionContexts.JSP_CONTENT)) {
// DWM: this logic is not incorrect ... given changes
// to adapters, etc.
// but probably don't need anything here, since both
// Java and JavaScript
// are supported in V5.
// nsd_TODO: verify this!!!
// INodeNotifier notifier =
// (INodeNotifier)model.getNode(region.getStartOffset());
// IAdapterFactory factory =
// model.getFactoryRegistry().getFactoryFor(ContentAssistAdapter.class);
// if(factory instanceof
// HTMLContentAssistAdapterFactory) {
// INodeAdapter adapter =
// ((HTMLContentAssistAdapterFactory)factory).createAdapter(notifier,
// region);
// if(adapter != null && adapter instanceof
// JSPJavaContentAssistAdapter)
if (regionStartOffset > startOffset)
return regionStartOffset;
else
return startOffset;
// }
}
// a custom tag, jsp:useBean, getproperty or setproperty
// statement is also a valid breakpoint location
else if (region.getType().equals(DOMRegionContext.XML_TAG_NAME) && (isCustomTagRegion(model.getIndexedRegion(regionStartOffset)) || regionContainer.getText(region).equals(JSP12Namespace.ElementName.USEBEAN) || regionContainer.getText(region).equals(JSP12Namespace.ElementName.GETPROPERTY) || regionContainer.getText(region).equals(JSP12Namespace.ElementName.SETPROPERTY))) {
if (regionStartOffset > startOffset)
return regionStartOffset;
else
return startOffset;
}
else {
// Defect #241090, the Text Nodes inside of JSP
// scriptlets, expressions, and declarations are valid
// breakpoint-able locations
boolean isCodeNode = false;
IndexedRegion node = model.getIndexedRegion(regionStartOffset);
if (node != null && node instanceof Node) {
Node domNode = (Node) node;
Node root = domNode.getOwnerDocument().getDocumentElement();
if (root != null && root.getNodeName().equals(JSP12Namespace.ElementName.ROOT) && domNode.getNodeType() == Node.TEXT_NODE && domNode.getParentNode() != null) {
String parentName = domNode.getParentNode().getNodeName();
isCodeNode = parentName.equals(JSP12Namespace.ElementName.SCRIPTLET) || parentName.equals(JSP12Namespace.ElementName.EXPRESSION) || parentName.equals(JSP12Namespace.ElementName.DECLARATION);
}
}
if (isCodeNode) {
if (regionStartOffset > startOffset)
return regionStartOffset;
else
return startOffset;
}
}
}
}
}
return NO_VALID_CONTENT;
}
private static boolean isCustomTagRegion(IndexedRegion node) {
if (node instanceof Element) {
Element xmlElement = (Element) node;
ModelQuery mq = ModelQueryUtil.getModelQuery(xmlElement.getOwnerDocument());
CMElementDeclaration decl = mq.getCMElementDeclaration(xmlElement);
if (decl instanceof CMNodeWrapper) {
CMNode cmNode = ((CMNodeWrapper) decl).getOriginNode();
return cmNode instanceof TLDElementDeclaration;
}
}
return false;
}
private ISourceEditingTextTools fSourceEditingTextTools;
protected IResource getEditorInputResource(IEditorInput input) {
IResource resource = (IResource) input.getAdapter(IFile.class);
if (resource == null) {
resource = (IResource) input.getAdapter(IResource.class);
}
return resource;
}
public ISourceEditingTextTools getSourceEditingTextTools() {
return fSourceEditingTextTools;
}
protected int getValidPosition(IDocument idoc, int lineNumber) {
if (!(getSourceEditingTextTools() instanceof IDOMSourceEditingTextTools)) {
return NO_VALID_CONTENT;
}
if (idoc == null)
return NO_VALID_CONTENT;
int startOffset, endOffset;
try {
startOffset = idoc.getLineOffset(lineNumber - 1);
endOffset = idoc.getLineOffset(lineNumber) - 1;
if (idoc == null)
return NO_VALID_CONTENT;
String lineText = idoc.get(startOffset, endOffset - startOffset).trim();
// blank lines or lines with only an open or close brace or
// scriptlet tag cannot have a breakpoint
if (lineText.equals("") || lineText.equals("{") || //$NON-NLS-2$//$NON-NLS-1$
lineText.equals("}") || lineText.equals("<%"))//$NON-NLS-2$//$NON-NLS-1$
return NO_VALID_CONTENT;
}
catch (BadLocationException e) {
return NO_VALID_CONTENT;
}
IStructuredDocumentRegion flatNode = ((IStructuredDocument) idoc).getRegionAtCharacterOffset(startOffset);
// go through the node's regions looking for JSP content
// until reaching the end of the line
while (flatNode != null) {
int validPosition = getValidRegionPosition(((IDOMDocument) ((IDOMSourceEditingTextTools) getSourceEditingTextTools()).getDOMDocument()).getModel(), flatNode, startOffset, endOffset);
if (validPosition == END_OF_LINE)
return NO_VALID_CONTENT;
if (validPosition >= 0)
return validPosition;
flatNode = flatNode.getNext();
}
return NO_VALID_CONTENT;
}
public void setSourceEditingTextTools(ISourceEditingTextTools sourceEditingTextTools) {
fSourceEditingTextTools = sourceEditingTextTools;
}
}