SourceInfo.java
001 /*
002  *  SourceInfo.java
003  *
004  *  Copyright (c) 1995-2012, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Mark A. Greenwood, 08/12/2010
013  *
014  */
015 
016 package gate.jape;
017 
018 import gate.util.Strings;
019 
020 import java.util.ArrayList;
021 import java.util.List;
022 
023 /**
024  * A simple class to store and use the mapping between Java and Jape
025  * source code for error reporting.
026  */
027 public class SourceInfo {
028 
029   private List<BlockInfo> blocks = new ArrayList<BlockInfo>();
030 
031   private String className = null;
032 
033   private String phaseName = null;
034 
035   private String sectionName = null;
036 
037   public SourceInfo(String className, String phaseName, String sectionName) {
038     this.className = className;
039     this.phaseName = phaseName;
040     this.sectionName = sectionName;
041   }
042 
043   public String addBlock(String previousCode, String codeBlock) {
044     if(!codeBlock.startsWith("  // JAPE Source:")) return codeBlock;
045 
046     String info = codeBlock.substring(18, codeBlock.indexOf("\n")).trim();
047     String code = codeBlock.substring(codeBlock.indexOf("\n"1);
048 
049     String japeURL = info.substring(0, info.lastIndexOf(":"));
050     int lineNumber = Integer
051             .parseInt(info.substring(info.lastIndexOf(":"1));
052 
053     int startLine = previousCode.split("\n").length + 1;
054     int endLine = startLine + code.split("\n").length;
055 
056     int startOffset = previousCode.length();
057     int endOffset = previousCode.length() + code.length();
058 
059     blocks.add(new BlockInfo(japeURL, lineNumber, startLine, endLine,
060             startOffset, endOffset));
061 
062     return code;
063   }
064 
065   public String getSource(String source, int javaLineNumber) {
066     for(BlockInfo info : blocks) {   
067       if(info.contains(javaLineNumber)) {
068         return info.getSource(source, info.getJapeLineNumber(javaLineNumber));
069       }
070     }
071 
072     return "";
073   }
074 
075   public StackTraceElement getStackTraceElement(int javaLineNumber) {
076     for(BlockInfo info : blocks) {
077       StackTraceElement japeSTE = info.getStackTraceElement(javaLineNumber);
078 
079       if(japeSTE != nullreturn japeSTE;
080     }
081 
082     return null;
083   }
084 
085   /**
086    * Enhances a Throwable by replacing mentions of Java code inside a
087    * Jape RhsAction with a reference to the original Jape source where
088    * available.
089    
090    @param t the Throwable to enhance with Jape source information
091    */
092   public void enhanceTheThrowable(Throwable t) {
093     if(t.getCause() != null) {
094       enhanceTheThrowable(t.getCause());
095     }
096     
097     List<StackTraceElement> stack = new ArrayList<StackTraceElement>();
098 
099     for(StackTraceElement ste : t.getStackTrace()) {
100       if(ste.getClassName().equals(className)) {
101 
102         StackTraceElement japeSTE = null;
103 
104         if(ste.getLineNumber() >= 0) {
105           for(BlockInfo info : blocks) {
106             japeSTE = info.getStackTraceElement(ste.getLineNumber());
107 
108             if(japeSTE != nullbreak;
109           }
110         }
111         else {
112           // this will happen if we are running from a
113           // serialised jape grammar as we don't keep the
114           // source info
115           japeSTE = new StackTraceElement(phaseName, sectionName, null, -1);
116         }
117 
118         stack.add(japeSTE != null ? japeSTE : ste);
119       }
120       else {
121         stack.add(ste);
122       }
123     }
124 
125     t.setStackTrace(stack.toArray(new StackTraceElement[stack.size()]));
126   }
127 
128   private class BlockInfo {
129     String japeURL;
130 
131     int japeLine;
132 
133     int startLine, endLine;
134 
135     int startOffset, endOffset;
136 
137     BlockInfo(String japeURL, int japeLine, int startLine, int endLine,
138             int startOffset, int endOffset) {
139 
140       this.japeURL = japeURL;
141       this.japeLine = japeLine;
142       this.startLine = startLine;
143       this.endLine = endLine;
144       this.startOffset = startOffset;
145       this.endOffset = endOffset;
146     }
147 
148     public boolean contains(int lineNumber) {
149       return (startLine <= lineNumber && lineNumber <= endLine);
150     }
151 
152     @SuppressWarnings("unused")
153     public String getNumberedSource(String source) {
154       return Strings.addLineNumbers(getSource(source), japeLine);
155     }
156 
157     public String getSource(String source, int line) {
158       String[] lines = getSource(source).split("\n");
159 
160       return lines[line - japeLine];
161     }
162 
163     public int getJapeLineNumber(int javaLineNumber) {
164       if(!contains(javaLineNumber)) return -1;
165 
166       return japeLine + (javaLineNumber - startLine);
167     }
168 
169     public StackTraceElement getStackTraceElement(int javaLineNumber) {
170       int japeLineNumber = getJapeLineNumber(javaLineNumber);
171 
172       if(japeLineNumber == -1return null;
173 
174       return new StackTraceElement(phaseName, sectionName, japeURL,
175               japeLineNumber);
176     }
177 
178     public String getSource(String source) {
179       return source.substring(startOffset, endOffset);
180     }
181   }
182 }