001 package gate.gui.jape;
002
003 import gate.Resource;
004 import gate.creole.ANNIEConstants;
005 import gate.creole.AbstractVisualResource;
006 import gate.creole.Transducer;
007 import gate.event.ProgressListener;
008 import gate.jape.parser.ParseCpslConstants;
009 import gate.jape.parser.ParseCpslTokenManager;
010 import gate.jape.parser.SimpleCharStream;
011 import gate.jape.parser.Token;
012 import gate.util.BomStrippingInputStreamReader;
013 import gate.util.GateRuntimeException;
014
015 import java.awt.BorderLayout;
016 import java.awt.Color;
017 import java.io.BufferedReader;
018 import java.io.IOException;
019 import java.io.InputStreamReader;
020 import java.io.Reader;
021 import java.io.StringReader;
022 import java.net.MalformedURLException;
023 import java.net.URL;
024 import java.util.ArrayList;
025 import java.util.HashMap;
026 import java.util.List;
027 import java.util.Map;
028
029 import javax.swing.JScrollPane;
030 import javax.swing.JTextPane;
031 import javax.swing.JTree;
032 import javax.swing.event.TreeSelectionEvent;
033 import javax.swing.event.TreeSelectionListener;
034 import javax.swing.text.Style;
035 import javax.swing.text.StyleConstants;
036 import javax.swing.text.StyledDocument;
037 import javax.swing.tree.DefaultMutableTreeNode;
038 import javax.swing.tree.DefaultTreeModel;
039 import javax.swing.tree.TreeSelectionModel;
040
041 /**
042 * A JAPE viewer that allows access to all phases of the grammar and
043 * provides syntax highlighting. Future versions may allow editing and
044 * reload of JAPE files.
045 *
046 * @author Mark A. Greenwood
047 */
048 public class JapeViewer extends AbstractVisualResource implements
049 ANNIEConstants,
050 ProgressListener {
051
052 /**
053 * The text area where the JAPE source will be displayed
054 */
055 private JTextPane textArea;
056
057 /**
058 * The tree in which the phases of the grammar will be shown
059 */
060 private JTree treePhases;
061
062 private JScrollPane treeScroll;
063
064 /**
065 * A flag so we can know if we are currently reading a highlighting a
066 * JAPE source file
067 */
068 private boolean updating = false;
069
070 /**
071 * The JAPE transducer for which we need to show the JAPE source
072 */
073 private Transducer transducer;
074
075 /**
076 * A map that associates the syntactic elements of JAPE files with a
077 * colour for performing syntax highlighting
078 */
079 private Map<Integer, Style> colorMap = new HashMap<Integer, Style>();
080
081 /**
082 * The default style used by the text area. This is used so that we
083 * can ensure that normal text is displayed normally. This fixes a
084 * problem where sometime the highlighting goes screwy and shows
085 * everything as a comment.
086 */
087 private Style defaultStyle;
088
089 @Override
090 public Resource init() {
091 initGuiComponents();
092 return this;
093 }
094
095 private void initGuiComponents() {
096 setLayout(new BorderLayout());
097 textArea = new JTextPane();
098 textArea.setEditable(false);
099 JScrollPane textScroll = new JScrollPane(textArea,
100 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
101 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
102 add(textScroll, BorderLayout.CENTER);
103
104 treePhases = new JTree();
105 treeScroll = new JScrollPane(treePhases,
106 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
107 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
108 add(treeScroll, BorderLayout.WEST);
109 treePhases.getSelectionModel().setSelectionMode(
110 TreeSelectionModel.SINGLE_TREE_SELECTION);
111 treePhases.addTreeSelectionListener(new TreeSelectionListener() {
112 public void valueChanged(TreeSelectionEvent e) {
113 if(updating) return;
114 if(e.getPath().getLastPathComponent() == null) return;
115
116 try {
117 readJAPEFileContents(new URL(transducer.getGrammarURL(), e.getPath()
118 .getLastPathComponent()
119 + ".jape"));
120 }
121 catch(MalformedURLException mue) {
122 mue.printStackTrace();
123 }
124 }
125 });
126
127 // if we want to set the jape to be monospaced (like most code
128 // editors) then
129 // do this...
130 /*
131 * MutableAttributeSet attrs = textArea.getInputAttributes();
132 * StyleConstants.setFontFamily(attrs, "monospaced"); StyledDocument
133 * doc = textArea.getStyledDocument(); doc.setCharacterAttributes(0,
134 * doc.getLength() + 1, attrs, false);
135 */
136 defaultStyle = textArea.addStyle("default", null);
137
138 Style style = textArea.addStyle("brackets", null);
139 StyleConstants.setForeground(style, Color.red);
140 colorMap.put(ParseCpslConstants.leftBrace, style);
141 colorMap.put(ParseCpslConstants.rightBrace, style);
142 colorMap.put(ParseCpslConstants.leftBracket, style);
143 colorMap.put(ParseCpslConstants.rightBracket, style);
144 colorMap.put(ParseCpslConstants.leftSquare, style);
145 colorMap.put(ParseCpslConstants.rightSquare, style);
146
147 style = textArea.addStyle("keywords", null);
148 StyleConstants.setForeground(style, Color.blue);
149 colorMap.put(ParseCpslConstants.rule, style);
150 colorMap.put(ParseCpslConstants.priority, style);
151 colorMap.put(ParseCpslConstants.macro, style);
152 colorMap.put(ParseCpslConstants.bool, style);
153 colorMap.put(ParseCpslConstants.phase, style);
154 colorMap.put(ParseCpslConstants.input, style);
155 colorMap.put(ParseCpslConstants.option, style);
156 colorMap.put(ParseCpslConstants.multiphase, style);
157 colorMap.put(ParseCpslConstants.phases, style);
158
159 style = textArea.addStyle("strings", null);
160 StyleConstants.setForeground(style, new Color(0, 128, 128));
161 colorMap.put(ParseCpslConstants.string, style);
162
163 style = textArea.addStyle("comments", null);
164 StyleConstants.setForeground(style, new Color(0, 128, 0));
165 colorMap.put(ParseCpslConstants.singleLineCStyleComment, style);
166 colorMap.put(ParseCpslConstants.singleLineCpslStyleComment, style);
167 colorMap.put(ParseCpslConstants.commentStart, style);
168 colorMap.put(ParseCpslConstants.commentChars, style);
169 colorMap.put(ParseCpslConstants.commentEnd, style);
170 colorMap.put(ParseCpslConstants.phasesSingleLineCStyleComment, style);
171 colorMap.put(ParseCpslConstants.phasesSingleLineCpslStyleComment, style);
172 colorMap.put(ParseCpslConstants.phasesCommentStart, style);
173 colorMap.put(ParseCpslConstants.phasesCommentChars, style);
174 colorMap.put(ParseCpslConstants.phasesCommentEnd, style);
175 }
176
177 @Override
178 public void setTarget(Object target) {
179 if(target == null || !(target instanceof Transducer)) {
180 throw new IllegalArgumentException(
181 "The GATE jape viewer can only be used with a GATE jape transducer!\n"
182 + target.getClass().toString()
183 + " is not a GATE Jape Transducer!");
184 }
185
186 if(transducer != null) {
187 transducer.removeProgressListener(this);
188 }
189
190 transducer = (Transducer)target;
191 URL japeFileURL = transducer.getGrammarURL();
192
193 if(japeFileURL == null) {
194 textArea.setText("The source for this JAPE grammar is not available!");
195 remove(treeScroll);
196 return;
197 }
198
199 String japePhaseName = japeFileURL.getFile();
200 japePhaseName = japePhaseName.substring(japePhaseName.lastIndexOf("/") + 1,
201 japePhaseName.length() - 5);
202 treePhases.setModel(new DefaultTreeModel(new DefaultMutableTreeNode(
203 japePhaseName)));
204 treePhases.setSelectionRow(0);
205
206 readJAPEFileContents(japeFileURL);
207 transducer.addProgressListener(this);
208 }
209
210 private void readJAPEFileContents(URL url) {
211 if(treePhases.getLastSelectedPathComponent() == null) return;
212 updating = true;
213
214 try {
215 Reader japeReader = null;
216 if(transducer.getEncoding() == null) {
217 japeReader = new BomStrippingInputStreamReader(url.openStream());
218 }
219 else {
220 japeReader = new BomStrippingInputStreamReader(url.openStream(), transducer
221 .getEncoding());
222 }
223 BufferedReader br = new BufferedReader(japeReader);
224 String content = br.readLine();
225 StringBuilder japeFileContents = new StringBuilder();
226 List<Integer> lineOffsets = new ArrayList<Integer>();
227
228 while(content != null) {
229 lineOffsets.add(japeFileContents.length());
230
231 // replace tabs with spaces otherwise the highlighting fails
232 // TODO work out why this is needed and fix it properly
233 japeFileContents.append(content.replaceAll("\t", " ")).append("\n");
234 content = br.readLine();
235 }
236
237 textArea.setText(japeFileContents.toString());
238 textArea.updateUI();
239 br.close();
240
241 ParseCpslTokenManager tokenManager = new ParseCpslTokenManager(
242 new SimpleCharStream(
243 new StringReader(japeFileContents.toString())));
244
245 StyledDocument doc = textArea.getStyledDocument();
246
247 doc.setCharacterAttributes(0, japeFileContents.length(), defaultStyle,
248 true);
249
250 ((DefaultMutableTreeNode)treePhases.getSelectionPath()
251 .getLastPathComponent()).removeAllChildren();
252
253 Token t;
254 while((t = tokenManager.getNextToken()).kind != 0) {
255
256 Token special = t.specialToken;
257 while(special != null) {
258 Style style = colorMap.get(special.kind);
259 if(style != null) {
260 int start = lineOffsets.get(special.beginLine - 1)
261 + special.beginColumn - 1;
262 int end = lineOffsets.get(special.endLine - 1) + special.endColumn
263 - 1;
264 doc.setCharacterAttributes(start, end - start + 1, style, true);
265 }
266
267 special = special.specialToken;
268 }
269
270 Style style = colorMap.get(t.kind);
271
272 if(style != null) {
273 int start = lineOffsets.get(t.beginLine - 1) + t.beginColumn - 1;
274 int end = lineOffsets.get(t.endLine - 1) + t.endColumn - 1;
275 doc.setCharacterAttributes(start, end - start + 1, style, true);
276 }
277
278 if(t.kind == ParseCpslConstants.path) {
279 ((DefaultMutableTreeNode)treePhases.getSelectionPath()
280 .getLastPathComponent()).add(new DefaultMutableTreeNode(t
281 .toString()));
282 }
283 }
284 }
285 catch(IOException ioe) {
286 throw new GateRuntimeException(ioe);
287 }
288
289 if(treePhases.getSelectionRows() != null
290 && treePhases.getSelectionRows().length > 0)
291 treePhases.expandRow(treePhases.getSelectionRows()[0]);
292
293 updating = false;
294 }
295
296 public void processFinished() {
297 setTarget(transducer);
298 }
299
300 public void progressChanged(int progress) {
301
302 }
303 }
|