/******************************************************************************* * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada. * 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: * The Chisel Group, University of Victoria * IBM Corporation *******************************************************************************/ package com.ibm.research.tagging.java.extractor; import java.util.ArrayList; import java.util.List; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.ISourceReference; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.ToolFactory; import org.eclipse.jdt.core.compiler.IScanner; import org.eclipse.jdt.core.compiler.ITerminalSymbols; import org.eclipse.jdt.core.compiler.InvalidInputException; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.rules.IRule; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.RuleBasedScanner; import org.eclipse.jface.text.rules.Token; import org.eclipse.jface.text.rules.WordRule; /** * * @author Mike * */ public class WaypointDefinitionExtractor { protected static final char TAG_START_CHAR = '@'; protected static final String TAG_STRING = "@tag"; protected static final char META_OPEN_CHAR = '['; protected static final String META_OPEN_STRING = "["; protected static final char META_CLOSE_CHAR = ']'; protected static final String META_CLOSE_STRING = "]"; /* * @tag todo : Get the line delimeter character from preferences and use this */ protected static final String EOL_STRING = "\n"; protected static final char EOL_CHAR = '\n'; protected static final String END_COMMENT_STRING = "*/"; /* * Token Keys */ protected static final Object TAG_KEY = new Object(); protected static final Object EOL_KEY = new Object(); protected static final Object END_COMMENT_KEY = new Object(); protected static final Object TAG_META_OPEN_KEY = new Object(); protected static final Object TAG_META_CLOSE_KEY = new Object(); private static RuleBasedScanner fWaypointScanner; static { fWaypointScanner = new RuleBasedScanner(); WordRule tagRule = new WordRule(new WaypointDetector()); tagRule.addWord(TAG_STRING, (IToken)new Token(TAG_KEY)); WordRule metaOpenRule = new WordRule(new MetaOpenDetector()); metaOpenRule.addWord(META_OPEN_STRING, (IToken)new Token(TAG_META_OPEN_KEY)); WordRule metaCloseRule = new WordRule(new MetaCloseDetector()); metaCloseRule.addWord(META_CLOSE_STRING, (IToken)new Token(TAG_META_CLOSE_KEY)); WordRule eolRule = new WordRule(new EndOfLineDetector()); eolRule.addWord(EOL_STRING, (IToken)new Token(EOL_KEY)); WordRule eocRule = new WordRule(new EndOfCommentDetector()); eocRule.addWord(END_COMMENT_STRING, (IToken)new Token(END_COMMENT_KEY)); fWaypointScanner.setRules(new IRule[]{tagRule,metaOpenRule,metaCloseRule,eolRule,eocRule}); } /** * Gets the tag regions associated with this entire document, if no tag regions * are found an empty array is returned * @param source reference * @return Array of tag regions */ public static IRegion[] getWaypointRegions(IDocument document) { return getWaypointRegions(document, 0, document.getLength()); } /** * Gets the tag regions associated with this document, restricted to the given source referece, if no tag regions * are found an empty array is returned * @param document * @param source reference * @return Array of tag regions */ public static IRegion[] getWaypointRegions(IDocument document, ISourceReference reference) { ISourceRange range; try { range = reference.getSourceRange(); } catch (JavaModelException e) { e.printStackTrace(); return new IRegion[0]; } if(range.getOffset() < 0 || range.getOffset() + range.getLength() > document.getLength()) return new IRegion[0]; return getWaypointRegions(document, range.getOffset(), range.getLength()); } /** * Gets the tag regions associated with this document from the given offset and length. * If no tag regions are found an empty array is returned. * @param source reference * @return Array of tag regions */ public static IRegion[] getWaypointRegions(IDocument document, int offset, int length) { IRegion[] commentRegions = getCommentRegions(document, offset, length); List<IRegion> tagRegions = new ArrayList<IRegion>(); // for each comment block get all the tags for (IRegion commentRegion : commentRegions) { IRegion[] regions = null; regions = internalGetWaypointRegions(document, commentRegion.getOffset(), commentRegion.getLength()); for (IRegion tagRegion : regions) { tagRegions.add(tagRegion); } } IRegion[] result= new IRegion[tagRegions.size()]; tagRegions.toArray(result); return result; } /** * Gets the comment regions associated with this document, restricted to the given offset and length, if no comment regions * are found an empty array is returned * @param document * @param offset * @param length * @param returnSingleLine Wither to collect single line commments * @return Array of tag regions */ public static IRegion[] getCommentRegions(IDocument document, int offset, int length) { try { StringBuffer content = new StringBuffer(document.get(offset,length)); IRegion range = new Region(offset,length); List<IRegion> commentRegions = new ArrayList<IRegion>(); IScanner scanner= ToolFactory.createScanner(true, false, false, false); scanner.setSource(content.toString().toCharArray()); int shift = range.getOffset(); while (true) { int terminal= scanner.getNextToken(); if (terminal == ITerminalSymbols.TokenNameCOMMENT_JAVADOC || terminal == ITerminalSymbols.TokenNameCOMMENT_BLOCK) { int commentOffset = shift + scanner.getCurrentTokenStartPosition(); int commentEnd = shift + scanner.getCurrentTokenEndPosition() + 1; commentRegions.add(new Region(commentOffset,commentEnd - commentOffset)); } else if(terminal == ITerminalSymbols.TokenNameCOMMENT_LINE) { int commentOffset = shift + scanner.getCurrentTokenStartPosition(); int commentEnd = shift + scanner.getCurrentTokenEndPosition() + 1; commentRegions.add(new Region(commentOffset,commentEnd - commentOffset)); } else if(terminal == ITerminalSymbols.TokenNameEOF) break; } IRegion[] result= new IRegion[commentRegions.size()]; commentRegions.toArray(result); return result; } catch (BadLocationException e) { e.printStackTrace(); } catch (InvalidInputException e) { e.printStackTrace(); } return new IRegion[0]; } /** * Gets the tag regions associated with this document, restricted to the given offset and length, if no tag regions * are found an empty array is returned, if a tag region overlaps the end of the region it will be ignored * @param document * @param offset * @param length * @return Array of tag regions */ // private static IRegion[] internalGetWaypointRegions(IDocument document, int offset, int length) // { // List<IRegion> tagRegions = new ArrayList<IRegion>(); // fWaypointScanner.setRange(document,offset,length); // // boolean tagDetected = false; // boolean eolDetected = false; // boolean metaOpenDetected = false; // // int tagOffset = 0; // int eolOffset = 0; // // while (true) // { // IToken token = fWaypointScanner.nextToken(); // // if(token.getData() == TAG_KEY) // { // /* We have already detected a tag start and eol so we have a single line tag */ // if(tagDetected && eolDetected) // { // tagRegions.add(new Region(tagOffset, eolOffset - tagOffset)); // /* reset eol detection */ // eolDetected = false; // } // // tagDetected = true; // tagOffset = fWaypointScanner.getTokenOffset(); // } // else if(token.getData() == EOL_KEY) // { // /* we only store the first eol after the tag start */ // if(tagDetected && !eolDetected) // { // eolDetected = true; // eolOffset = fWaypointScanner.getTokenOffset(); // } // } // else if(token.getData() == TAG_META_OPEN_KEY) // { // metaOpenDetected = true; // } // else if(token.getData() == TAG_META_CLOSE_KEY) // { // /* // * We reached a meta end and we have already found a tag and meta open // * so we have a multi line tag // */ // if(tagDetected && metaOpenDetected) // { // tagRegions.add(new Region(tagOffset, (fWaypointScanner.getTokenOffset() + 1) - tagOffset)); // } // // tagDetected = false; // eolDetected = false; // metaOpenDetected = false; // } // else if(token.getData() == END_COMMENT_KEY) // { // /* We have already detected a tag start and eol so we have a tag in there somewhere */ // if(tagDetected && eolDetected) // tagRegions.add(new Region(tagOffset, (eolOffset) - tagOffset)); // /* We have already detected a tag start but no eol so we have a single line tag */ // else if(tagDetected) // tagRegions.add(new Region(tagOffset, (fWaypointScanner.getTokenOffset()) - tagOffset)); // // break; // } // else if(token.equals(Token.EOF)) // { // /* We have already detected a tag start and eol so we have a single line tag */ // if(tagDetected && eolDetected) // tagRegions.add(new Region(tagOffset, (eolOffset) - tagOffset)); // else // /* Real edge case here, a rogue tag, assume its a single line without line delimeter*/ // if(tagDetected) // tagRegions.add(new Region(tagOffset, (fWaypointScanner.getTokenOffset()) - tagOffset)); // // break; // } // } // // IRegion[] result= new IRegion[tagRegions.size()]; // tagRegions.toArray(result); // return result; // } // Wwe only support single line tags, the method above will pull multi line tags private static IRegion[] internalGetWaypointRegions(IDocument document, int offset, int length) { List<IRegion> tagRegions = new ArrayList<IRegion>(); fWaypointScanner.setRange(document,offset,length); boolean tagDetected = false; boolean metaOpenDetected = false; int tagOffset = 0; int eolOffset = 0; while (true) { IToken token = fWaypointScanner.nextToken(); if(token.getData() == TAG_KEY) { tagDetected = true; tagOffset = fWaypointScanner.getTokenOffset(); } else if(token.getData() == EOL_KEY) { /* we only store the first eol after the tag start */ if(tagDetected) { eolOffset = fWaypointScanner.getTokenOffset(); tagRegions.add(new Region(tagOffset, eolOffset - tagOffset)); tagDetected = false; } } else if(token.getData() == TAG_META_OPEN_KEY) { metaOpenDetected = true; } else if(token.getData() == TAG_META_CLOSE_KEY) { /* * We reached a meta end and we have already found a tag and meta open * so we have a multi line tag */ if(tagDetected && metaOpenDetected) { tagRegions.add(new Region(tagOffset, (fWaypointScanner.getTokenOffset() + 1) - tagOffset)); } tagDetected = false; metaOpenDetected = false; } else if(token.getData() == END_COMMENT_KEY) { if(tagDetected) tagRegions.add(new Region(tagOffset, (fWaypointScanner.getTokenOffset()) - tagOffset)); break; } else if(token.equals(Token.EOF)) { if(tagDetected) tagRegions.add(new Region(tagOffset, (fWaypointScanner.getTokenOffset()) - tagOffset)); break; } } IRegion[] result= new IRegion[tagRegions.size()]; tagRegions.toArray(result); return result; } }