| /******************************************************************************* |
| * 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; |
| } |
| } |