blob: c6faff3c4a948cdf7b060b5ad75ab7cebb4755e4 [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.jsdt.web.core.internal.java;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.Position;
import org.eclipse.wst.jsdt.core.IBuffer;
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.xml.core.internal.provisional.document.IDOMModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
/**
* Translates a JSP document into a HttpServlet. Keeps two way mapping from java
* translation to the original JSP source, which can be obtained through
* getJava2JspRanges() and getJsp2JavaRanges().
*
* @author pavery
*/
public class JsTranslator extends Job implements IDocumentListener{
private static final boolean DEBUG;
private static final boolean DEBUG_SAVE_OUTPUT = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.wst.jsdt.web.core/debug/jsptranslationstodisk")); //$NON-NLS-1$ //$NON-NLS-2$
public static final String ENDL = "\n"; //$NON-NLS-1$
static {
String value = Platform.getDebugOption("org.eclipse.wst.jsdt.web.core/debug/jspjavamapping"); //$NON-NLS-1$
DEBUG = value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$
}
private IStructuredDocumentRegion fCurrentNode;
private StringBuffer fScriptText = new StringBuffer();
private IStructuredDocument fStructuredDocument = null;
private ArrayList importLocationsInHtml = new ArrayList();
/* use java script by default */
private boolean isGlobalJs = true;
private ArrayList rawImports = new ArrayList(); // traslated
private ArrayList scriptLocationInHtml = new ArrayList();
private int scriptOffset = 0;
private byte[] fLock = new byte[0];
private byte[] finished = new byte[0];
private IBuffer compUnitBuff;
private boolean cancelParse = false;
private int missingEndTagRegionStart = -1;
private static final boolean ADD_SEMICOLON_AT_INLINE=true;
private void advanceNextNode() {
setCurrentNode(getCurrentNode().getNext());
}
public JsTranslator(IStructuredDocument document, String fileName) {
super("JavaScript translation for : " + fileName);
fStructuredDocument = document;
fStructuredDocument.addDocumentListener(this);
setPriority(Job.LONG);
setSystem(true);
schedule();
reset();
}
public JsTranslator(IStructuredDocument document, String fileName, boolean listenForChanges) {
super("JavaScript translation for : " + fileName);
fStructuredDocument = document;
if(listenForChanges) {
fStructuredDocument.addDocumentListener(this);
setPriority(Job.LONG);
setSystem(true);
schedule();
}
reset();
}
public String getJsText() {
synchronized(finished) {
return fScriptText.toString();
}
}
final public IStructuredDocumentRegion getCurrentNode() {
return fCurrentNode;
}
public void setBuffer(IBuffer buffer) {
compUnitBuff = buffer;
synchronized(finished) {
compUnitBuff.setContents(fScriptText.toString());
}
}
public Position[] getHtmlLocations() {
synchronized(finished) {
return (Position[]) scriptLocationInHtml.toArray(new Position[scriptLocationInHtml.size()]);
}
}
public int getMissingEndTagRegionStart() {
return missingEndTagRegionStart;
}
public Position[] getImportHtmlRanges() {
synchronized(finished) {
return (Position[]) importLocationsInHtml.toArray(new Position[importLocationsInHtml.size()]);
}
}
private char[] getPad(int numberOfChars) {
final char[] spaceArray = new char[numberOfChars];
Arrays.fill(spaceArray, ' ');
return spaceArray;
}
public String[] getRawImports() {
synchronized(finished) {
return (String[]) this.rawImports.toArray(new String[rawImports.size()]);
}
}
/**
*
* @return the status of the translator's progrss monitor, false if the
* monitor is null
*/
private boolean isCanceled() {
return cancelParse;
}
/**
* Reinitialize some fields
*/
private void reset() {
synchronized(fLock) {
scriptOffset = 0;
// reset progress monitor
cancelParse = false;
fScriptText = new StringBuffer();
fCurrentNode = fStructuredDocument.getFirstStructuredDocumentRegion();
rawImports.clear();
importLocationsInHtml.clear();
scriptLocationInHtml.clear();
missingEndTagRegionStart = -1;
}
translate();
}
private IStructuredDocumentRegion setCurrentNode(IStructuredDocumentRegion currentNode) {
synchronized(fLock) {
return this.fCurrentNode = currentNode;
}
}
public void translate() {
//setCurrentNode(fStructuredDocument.getFirstStructuredDocumentRegion());
synchronized(finished) {
while (getCurrentNode() != null && !isCanceled()) {
// System.out.println("Translator Looking at Node
// type:"+getCurrentNode().getType()+"---------------------------------:");
// System.out.println(new NodeHelper(getCurrentNode()));
// i.println("/---------------------------------------------------");
if (getCurrentNode().getType() == DOMRegionContext.XML_TAG_NAME) {
NodeHelper nh = new NodeHelper(getCurrentNode());
if ((!nh.isEndTag() || nh.isSelfClosingTag()) && nh.nameEquals("script")) {
/*
* Handles the following cases: <script
* type="javascriptype"> <script language="javascriptype>
* <script src='' type=javascriptype> <script src=''
* language=javascripttype <script src=''> global js type.
* <script> (global js type)
*/
if (NodeHelper.isInArray(JsDataTypes.JSVALIDDATATYPES, nh.getAttributeValue("type")) || NodeHelper.isInArray(JsDataTypes.JSVALIDDATATYPES, nh.getAttributeValue("language")) || isGlobalJs) {
if (nh.containsAttribute(new String[] { "src" })) {
// Handle import
translateScriptImportNode(getCurrentNode());
}
// } else {
// handle script section
if (getCurrentNode().getNext() != null /*&& getCurrentNode().getNext().getType() == DOMRegionContext.BLOCK_TEXT*/) {
translateJSNode(getCurrentNode().getNext());
}
} // End search for <script> sections
} else if (nh.containsAttribute(JsDataTypes.HTMLATREVENTS)) {
/* Check for embeded JS events in any tags */
translateInlineJSNode(getCurrentNode());
} else if (nh.nameEquals("META") && nh.attrEquals("http-equiv", "Content-Script-Type") && nh.containsAttribute(new String[] { "content" })) {
// <META http-equiv="Content-Script-Type" content="type">
isGlobalJs = NodeHelper.isInArray(JsDataTypes.JSVALIDDATATYPES, nh.getAttributeValue("content"));
} // End big if of JS types
}
if (getCurrentNode() != null) {
advanceNextNode();
}
} // end while loop
}
finishedTranslation();
}
private void finishedTranslation() {
if(compUnitBuff!=null) compUnitBuff.setContents(fScriptText.toString());
}
public void translateInlineJSNode(IStructuredDocumentRegion container) {
// System.out
// .println("JSPTranslator.translateInlineJSNode Entered
// w/ScriptOffset:"
// + scriptOffset);
NodeHelper nh = new NodeHelper(container);
// System.out.println("inline js node looking at:\n" + nh);
/* start a function header.. will amend later */
ITextRegionList t = container.getRegions();
ITextRegion r;
Iterator regionIterator = t.iterator();
while (regionIterator.hasNext() && !isCanceled() ) {
r = (ITextRegion) regionIterator.next();
if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
int start = r.getStart();
int offset = r.getTextEnd();
String tagAttrname = container.getText().substring(start, offset).trim();
/*
* Attribute values aren't case sensative, also make sure next
* region is attrib value
*/
if (NodeHelper.isInArray(JsDataTypes.HTMLATREVENTS, tagAttrname)) {
if (regionIterator.hasNext()) {
regionIterator.next();
}
if (regionIterator.hasNext()) {
r = ((ITextRegion) regionIterator.next());
}
if (r.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
int valStartOffset = container.getStartOffset(r);
// int valEndOffset = r.getTextEnd();
String rawText = container.getText().substring(r.getStart(), r.getTextEnd());
if (rawText == null || rawText.length() == 0) {
return;
}
/* Strip quotes */
switch (rawText.charAt(0)) {
case '\'':
case '"':
rawText = rawText.substring(1);
valStartOffset++;
}
switch (rawText.charAt(rawText.length() - 1)) {
case '\'':
case '"':
rawText = rawText.substring(0, rawText.length() - 1);
}
// Position inScript = new Position(scriptOffset,
// rawText.length());
/* Quoted text starts +1 and ends -1 char */
if(ADD_SEMICOLON_AT_INLINE) rawText = rawText + ";";
Position inHtml = new Position(valStartOffset, rawText.length());
scriptLocationInHtml.add(inHtml);
/* need to pad the script text with spaces */
char[] spaces = getPad(valStartOffset - scriptOffset);
fScriptText.append(spaces);
fScriptText.append(rawText);
scriptOffset = fScriptText.length();
}
}
}
}
}
public void translateJSNode(IStructuredDocumentRegion container) {
ITextRegionCollection containerRegion = container;
Iterator regions = containerRegion.getRegions().iterator();
ITextRegion region = null;
if(container==null) return;
char[] spaces = getPad(container.getStartOffset() - scriptOffset);
fScriptText.append(spaces);
scriptOffset = container.getStartOffset();
if(container.getType()!=DOMRegionContext.BLOCK_TEXT && container.getType()!= DOMRegionContext.XML_CDATA_TEXT) {
return;
}
while (regions.hasNext() && !isCanceled()) {
region = (ITextRegion) regions.next();
String type = region.getType();
// content assist was not showing up in JSP inside a javascript
// region
if (type == DOMRegionContext.BLOCK_TEXT) {
int scriptStart = container.getStartOffset();
int scriptTextEnd = container.getEndOffset() - container.getStartOffset();
String regionText = container.getText().substring(region.getStart(), region.getEnd());
int regionLength = regionText.length();
// /Position inScript = new Position(scriptOffset,
// regionLength);
Position inHtml = new Position(scriptStart, scriptTextEnd);
scriptLocationInHtml.add(inHtml);
spaces = getPad(scriptStart - scriptOffset);
fScriptText.append(spaces);
// fJsToHTMLRanges.put(inScript, inHtml);
fScriptText.append(regionText);
scriptOffset = fScriptText.length();
}
}
IStructuredDocumentRegion endTag = container.getNext();
if(endTag==null) {
missingEndTagRegionStart = container.getStartOffset();
}else if(endTag!=null) {
NodeHelper nh = new NodeHelper(endTag);
String name = nh.getTagName();
if(name==null || !name.trim().equalsIgnoreCase("script") || !nh.isEndTag()) {
missingEndTagRegionStart = container.getStartOffset();
}
}
}
public void translateScriptImportNode(IStructuredDocumentRegion region) {
NodeHelper nh = new NodeHelper(region);
String importName = nh.getAttributeValue("src");
if (importName != null && !importName.equals("")) {
rawImports.add(importName);
Position inHtml = new Position(region.getStartOffset(), region.getEndOffset());
importLocationsInHtml.add(inHtml);
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
*/
public void documentAboutToBeChanged(DocumentEvent event) {
cancelParse = true;
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
*/
public void documentChanged(DocumentEvent event) {
reset();
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
protected IStatus run(IProgressMonitor monitor) {
return Status.OK_STATUS;
}
public void release() {
fStructuredDocument.removeDocumentListener(this);
}
}