/plugins/JavaSideKick/tags/javasidekick-2-0-2/src/sidekick/java/JavaParser.java

# · Java · 523 lines · 358 code · 62 blank · 103 comment · 84 complexity · 45ad7486b2c7d5bf0118d78c48cf48d6 MD5 · raw file

  1. /*
  2. Copyright (c) 2005, Dale Anson
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without modification,
  5. are permitted provided that the following conditions are met:
  6. * Redistributions of source code must retain the above copyright notice,
  7. this list of conditions and the following disclaimer.
  8. * Redistributions in binary form must reproduce the above copyright notice,
  9. this list of conditions and the following disclaimer in the documentation
  10. and/or other materials provided with the distribution.
  11. * Neither the name of the <ORGANIZATION> nor the names of its contributors
  12. may be used to endorse or promote products derived from this software without
  13. specific prior written permission.
  14. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  15. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  16. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  17. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  18. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  19. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  20. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  21. ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  23. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. package sidekick.java;
  26. import java.awt.event.*;
  27. import java.io.*;
  28. //import java.lang.reflect.*;
  29. import java.util.*;
  30. import java.util.regex.*;
  31. import javax.swing.text.Position;
  32. import javax.swing.tree.DefaultMutableTreeNode;
  33. import sidekick.java.node.*;
  34. import sidekick.java.options.*;
  35. import sidekick.java.parser.*;
  36. import org.gjt.sp.jedit.*;
  37. import org.gjt.sp.jedit.msg.*;
  38. import org.gjt.sp.util.*;
  39. import errorlist.*;
  40. import sidekick.SideKickCompletion;
  41. import sidekick.SideKickParser;
  42. import sidekick.SideKickParsedData;
  43. public class JavaParser extends SideKickParser implements EBComponent {
  44. private View currentView = null;
  45. private JBrowseOptionDialog optionDialog;
  46. private GeneralOptions options;
  47. private MutableFilterOptions filterOpt;
  48. private MutableDisplayOptions displayOpt;
  49. private boolean sorted = true; // are the tree nodes sorted by type?
  50. private JavaCompletionFinder completionFinder = null;
  51. public JavaParser() {
  52. super( "java" );
  53. loadOptions();
  54. }
  55. private void loadOptions() {
  56. options = new GeneralOptions();
  57. options.load( new JEditPropertyAccessor() );
  58. filterOpt = options.getFilterOptions();
  59. displayOpt = options.getDisplayOptions();
  60. // re-init the labeler
  61. TigerLabeler.setDisplayOptions( displayOpt );
  62. }
  63. /**
  64. * This method is called when a buffer using this parser is selected
  65. * in the specified view.
  66. * @param editPane The edit pane
  67. * @since SideKick 0.3.1
  68. */
  69. public void activate( EditPane editPane ) {
  70. super.activate( editPane );
  71. currentView = editPane.getView();
  72. EditBus.addToBus( this );
  73. }
  74. public void deactivate( EditPane editPane ) {
  75. super.deactivate( editPane );
  76. EditBus.removeFromBus( this );
  77. }
  78. public void handleMessage( EBMessage msg ) {
  79. if ( msg instanceof PropertiesChanged ) {
  80. loadOptions();
  81. parse();
  82. }
  83. }
  84. public void parse() {
  85. if ( currentView != null ) {
  86. parse( currentView.getBuffer(), null );
  87. }
  88. }
  89. /**
  90. * Parse the contents of the given buffer.
  91. * @param buffer the buffer to parse
  92. */
  93. public SideKickParsedData parse( Buffer buffer, DefaultErrorSource errorSource ) {
  94. // re-init the labeler
  95. TigerLabeler.setDisplayOptions( displayOpt );
  96. String filename = buffer.getPath();
  97. SideKickParsedData parsedData = new JavaSideKickParsedData( filename );
  98. DefaultMutableTreeNode root = parsedData.root;
  99. if ( buffer.getLength() <= 0 )
  100. return parsedData;
  101. // read the source code directly from the Buffer rather than from the
  102. // file. This means:
  103. // 1) a modifed buffer can be parsed without a save
  104. // 2) reading the buffer should be faster than reading from the file, and
  105. // 3) jEdit has that 'gzip file on disk' option which won't parse.
  106. ByteArrayInputStream input = new ByteArrayInputStream( buffer.getText( 0, buffer.getLength() ).getBytes() );
  107. TigerParser parser = new TigerParser( input );
  108. try {
  109. CUNode compilationUnit = parser.CompilationUnit( buffer.getTabSize() ); // pass tab size so parser can set column offsets accurately
  110. compilationUnit.setName( buffer.getName() );
  111. compilationUnit.setResults( parser.getResults() );
  112. compilationUnit.setStart( createStartPosition( buffer, compilationUnit ) );
  113. compilationUnit.setEnd( createEndPosition( buffer, compilationUnit ) );
  114. root.setUserObject( compilationUnit );
  115. if ( compilationUnit.getChildren() != null ) {
  116. Collections.sort( compilationUnit.getChildren(), nodeSorter );
  117. for ( Iterator it = compilationUnit.getChildren().iterator(); it.hasNext(); ) {
  118. TigerNode child = ( TigerNode ) it.next();
  119. child.setStart( createStartPosition( buffer, child ) );
  120. child.setEnd( createEndPosition( buffer, child ) );
  121. if ( canShow( child ) ) {
  122. DefaultMutableTreeNode cuChild = new DefaultMutableTreeNode( child );
  123. root.add( cuChild );
  124. addChildren( buffer, cuChild, child );
  125. }
  126. }
  127. }
  128. }
  129. catch ( ParseException e ) {
  130. if ( displayOpt.getShowErrors() ) {
  131. ErrorNode eu = new ErrorNode( e );
  132. eu.setName( buffer.getName() );
  133. root.setUserObject( eu );
  134. String msg = e.getMessage();
  135. boolean isJava = buffer.getName().endsWith( ".java" );
  136. if ( !isJava )
  137. msg += ( " - Not a java file?" );
  138. root.add( new DefaultMutableTreeNode( "<html><font color=red>" + msg ) );
  139. Location loc = getExceptionLocation( e );
  140. errorSource.addError( ErrorSource.ERROR, buffer.getPath(), loc.line, loc.column, loc.column, e.getMessage() + ( isJava ? "" : " - Not a java file?" ) );
  141. }
  142. }
  143. finally {
  144. try {
  145. input.close();
  146. }
  147. catch ( Exception e ) {
  148. // not to worry
  149. }
  150. }
  151. handleErrors( errorSource, parser, buffer );
  152. return parsedData;
  153. }
  154. // the parser accumulates errors as it parses. This method passed them all to
  155. // the ErrorList plugin.
  156. private void handleErrors( DefaultErrorSource errorSource, TigerParser parser, Buffer buffer ) {
  157. if ( displayOpt.getShowErrors() ) {
  158. for ( Iterator it = parser.getErrors().iterator(); it.hasNext(); ) {
  159. ErrorNode en = ( ErrorNode ) it.next();
  160. Exception e = en.getException();
  161. ParseException pe = null;
  162. Location loc = new Location( 0, 0 );
  163. if ( e instanceof ParseException ) {
  164. pe = ( ParseException ) e;
  165. loc = getExceptionLocation( pe );
  166. }
  167. errorSource.addError( ErrorSource.ERROR, buffer.getPath(), loc.line, loc.column, loc.column, e.getMessage() );
  168. }
  169. }
  170. }
  171. private void addChildren( Buffer buffer, DefaultMutableTreeNode parent, TigerNode tn ) {
  172. if ( tn.getChildCount() > 0 ) {
  173. List children = tn.getChildren();
  174. Collections.sort( children, nodeSorter );
  175. for ( Iterator it = children.iterator(); it.hasNext(); ) {
  176. TigerNode child = ( TigerNode ) it.next();
  177. child.setStart( createStartPosition( buffer, child ) );
  178. child.setEnd( createEndPosition( buffer, child ) );
  179. if ( canShow( child ) ) {
  180. DefaultMutableTreeNode treeNode = new DefaultMutableTreeNode( child );
  181. parent.add( treeNode );
  182. if ( child.getChildren() != null && child.getChildren().size() > 0 ) {
  183. addChildren( buffer, treeNode, child );
  184. }
  185. }
  186. else {
  187. // need to fill in start and end positions
  188. setChildPositions( buffer, child );
  189. }
  190. }
  191. }
  192. }
  193. private void setChildPositions( Buffer buffer, TigerNode tn ) {
  194. for ( int i = 0; i < tn.getChildCount(); i++ ) {
  195. TigerNode child = tn.getChildAt( i );
  196. child.setStart( createStartPosition( buffer, child ) );
  197. child.setEnd( createEndPosition( buffer, child ) );
  198. setChildPositions( buffer, child );
  199. }
  200. }
  201. /**
  202. * Need to create Positions for each node. The javacc parser finds line and
  203. * column location, need to convert this to a Position in the buffer. The
  204. * TigerNode contains a column offset based on the current tab size as set in
  205. * the Buffer, need to use getOffsetOfVirtualColumn to account for soft and
  206. * hard tab handling.
  207. */
  208. private Position createStartPosition( Buffer buffer, TigerNode child ) {
  209. final int line_offset = buffer.getLineStartOffset( Math.max( child.getStartLocation().line - 1, 0 ) );
  210. final int col_offset = buffer.getOffsetOfVirtualColumn( Math.max( child.getStartLocation().line - 1, 0 ),
  211. Math.max( child.getStartLocation().column - 1, 0 ), null );
  212. return new Position() {
  213. public int getOffset() {
  214. return line_offset + col_offset;
  215. }
  216. };
  217. }
  218. /**
  219. * Need to create Positions for each node. The javacc parser finds line and
  220. * column location, need to convert this to a Position in the buffer. The
  221. * TigerNode contains a column offset based on the current tab size as set in
  222. * the Buffer, need to use getOffsetOfVirtualColumn to account for soft and
  223. * hard tab handling.
  224. */
  225. private Position createEndPosition( Buffer buffer, TigerNode child ) {
  226. final int line_offset = buffer.getLineStartOffset( Math.max( child.getEndLocation().line - 1, 0 ) );
  227. final int col_offset = buffer.getOffsetOfVirtualColumn( Math.max( child.getEndLocation().line - 1, 0 ),
  228. Math.max( child.getEndLocation().column - 1, 0 ), null );
  229. return new Position() {
  230. public int getOffset() {
  231. return line_offset + col_offset;
  232. }
  233. };
  234. }
  235. /**
  236. * @return attempts to return a Location indicating the location of a parser
  237. * exception. If the ParseException contains a Token reference, all is well,
  238. * otherwise, this method attempts to parse the message string for the
  239. * exception.
  240. */
  241. private Location getExceptionLocation( ParseException pe ) {
  242. Token t = pe.currentToken;
  243. if ( t != null ) {
  244. return new Location( t.next.beginLine - 1, t.next.beginColumn );
  245. }
  246. // ParseException message look like: "Parse error at line 116, column 5. Encountered: }"
  247. try {
  248. Pattern p = Pattern.compile( "(.*?)(\\d+)(.*?)(\\d+)(.*?)" );
  249. Matcher m = p.matcher( pe.getMessage() );
  250. if ( m.matches() ) {
  251. String ln = m.group( 2 );
  252. String cn = m.group( 4 );
  253. int line_number = -1;
  254. int column_number = 0;
  255. if ( ln != null )
  256. line_number = Integer.parseInt( ln );
  257. if ( cn != null )
  258. column_number = Integer.parseInt( cn );
  259. return line_number > -1 ? new Location( line_number - 1, column_number ) : null;
  260. }
  261. return new Location( 0, 0 );
  262. }
  263. catch ( Exception e ) {
  264. e.printStackTrace();
  265. return new Location( 0, 0 );
  266. }
  267. }
  268. // single place to check the filter settings, that is, check to see if it
  269. // is okay to show a particular node
  270. private boolean canShow( TigerNode node ) {
  271. if ( !isVisible( node ) )
  272. return false;
  273. if ( node.getOrdinal() == TigerNode.BLOCK )
  274. return false;
  275. if ( node.getOrdinal() == TigerNode.INITIALIZER )
  276. return filterOpt.getShowInitializers();
  277. if ( node.getOrdinal() == TigerNode.EXTENDS )
  278. return filterOpt.getShowGeneralizations();
  279. if ( node.getOrdinal() == TigerNode.IMPLEMENTS )
  280. return filterOpt.getShowGeneralizations();
  281. if ( node.getOrdinal() == TigerNode.FIELD && filterOpt.getShowFields() ) {
  282. if ( ( ( FieldNode ) node ).isPrimitive() )
  283. return filterOpt.getShowPrimitives();
  284. return true;
  285. }
  286. if ( node.getOrdinal() == TigerNode.VARIABLE ) {
  287. return filterOpt.getShowVariables();
  288. }
  289. if ( node.getOrdinal() == TigerNode.THROWS )
  290. return filterOpt.getShowThrows();
  291. return true;
  292. }
  293. // check if a node should be visible based on the 'top level' or 'member visible' settings
  294. private boolean isVisible( TigerNode tn ) {
  295. if ( ( tn.getOrdinal() == TigerNode.CLASS || tn.getOrdinal() == TigerNode.INTERFACE ) && tn.getParent() != null && tn.getParent().getOrdinal() == TigerNode.COMPILATION_UNIT ) {
  296. int visible_level = filterOpt.getTopLevelVisIndex();
  297. switch ( visible_level ) {
  298. case MutableModifier.TOPLEVEL_VIS_PUBLIC:
  299. return ModifierSet.isPublic( tn.getModifiers() );
  300. case MutableModifier.TOPLEVEL_VIS_PACKAGE:
  301. return true;
  302. }
  303. }
  304. int visible_level = filterOpt.getMemberVisIndex();
  305. switch ( visible_level ) {
  306. case MutableModifier.MEMBER_VIS_PACKAGE:
  307. return ModifierSet.isPackage( tn.getModifiers() ) || ModifierSet.isProtected( tn.getModifiers() ) || ModifierSet.isPublic( tn.getModifiers() );
  308. case MutableModifier.MEMBER_VIS_PROTECTED:
  309. return ModifierSet.isProtected( tn.getModifiers() ) || ModifierSet.isPublic( tn.getModifiers() );
  310. case MutableModifier.MEMBER_VIS_PUBLIC:
  311. return ModifierSet.isPublic( tn.getModifiers() );
  312. default:
  313. return true;
  314. }
  315. }
  316. private Comparator nodeSorter = new Comparator() {
  317. String LINE = jEdit.getProperty( "options.sidekick.java.sortByLine", "Line" );
  318. String NAME = jEdit.getProperty( "options.sidekick.java.sortByName", "Name" );
  319. String VISIBILITY = jEdit.getProperty( "options.sidekick.java.sortByVisibility", "Visibility" );
  320. /**
  321. * Compares a TigerNode to another TigerNode for sorting. Sorting may be by
  322. * line number or node type as determined by the value of "sorted".
  323. * @param o a TigerNode to compare to this node.
  324. * @return a negative integer, zero, or a positive integer as this TigerNode is
  325. * less than, equal to, or greater than the specified TigerNode.
  326. */
  327. public int compare( Object a, Object b ) {
  328. if ( ! ( a instanceof TigerNode ) )
  329. return -1;
  330. if ( ! ( b instanceof TigerNode ) )
  331. return 1;
  332. TigerNode tna = ( TigerNode ) a;
  333. TigerNode tnb = ( TigerNode ) b;
  334. String sortBy = displayOpt.getSortBy();
  335. if ( LINE.equals( sortBy ) ) {
  336. // sort by line
  337. Integer my_line = new Integer( tna.getStartLocation().line );
  338. Integer other_line = new Integer( tnb.getStartLocation().line );
  339. return my_line.compareTo( other_line );
  340. }
  341. else if ( VISIBILITY.equals( sortBy ) ) {
  342. Integer my_vis = new Integer( ModifierSet.visibilityRank( tna.getModifiers() ) );
  343. Integer other_vis = new Integer( ModifierSet.visibilityRank( tnb.getModifiers() ) );
  344. int comp = my_vis.compareTo( other_vis );
  345. return comp == 0 ? compareNames( tna, tnb ) : comp;
  346. }
  347. else {
  348. // sort by name
  349. return compareNames( tna, tnb );
  350. }
  351. }
  352. private int compareNames( TigerNode tna, TigerNode tnb ) {
  353. // sort by name
  354. Integer my_ordinal = new Integer( tna.getOrdinal() );
  355. Integer other_ordinal = new Integer( tnb.getOrdinal() );
  356. int comp = my_ordinal.compareTo( other_ordinal );
  357. return comp == 0 ? tna.getName().compareTo( tnb.getName() ) : comp;
  358. }
  359. };
  360. /******************************************************************************/
  361. // taken from jbrowse.JBrowse
  362. /******************************************************************************/
  363. private class StatusBarOptionAction implements ActionListener {
  364. public void actionPerformed( ActionEvent e ) {
  365. JavaParser.this.setStatusVisible(
  366. JavaParser.this.options.getShowStatusBar()
  367. );
  368. }
  369. }
  370. public boolean isStatusVisible() {
  371. return false;
  372. ///return statusPanel.isVisible();
  373. }
  374. public void setStatusVisible( boolean visible ) {
  375. ///statusPanel.setVisible( visible );
  376. }
  377. private ActionListener statusBarOptionAction = null;
  378. public ActionListener getStatusBarOptionAction() {
  379. if ( statusBarOptionAction == null ) {
  380. statusBarOptionAction = new StatusBarOptionAction();
  381. }
  382. return statusBarOptionAction;
  383. }
  384. public ActionListener getResizeAction() {
  385. return null; /// might want to implement this
  386. }
  387. // sorting is by line or by node type, with node type being the initial default.
  388. // The sort type is signalled by setting a System property.
  389. private ActionListener sortOptionAction = null;
  390. public ActionListener getSortOptionAction() {
  391. if ( sortOptionAction == null ) {
  392. sortOptionAction = new SortOptionAction();
  393. }
  394. return sortOptionAction;
  395. }
  396. private class SortOptionAction implements ActionListener {
  397. public void actionPerformed( ActionEvent e ) {
  398. // on action, toggle between unsorted (sort by line number) or
  399. // or sorted (sort by ordinal);
  400. sorted = !sorted;
  401. if ( currentView != null )
  402. parse( currentView.getBuffer(), null );
  403. }
  404. }
  405. // controls what is visible/displayed in the tree
  406. private ActionListener filterOptionAction = null;
  407. public ActionListener getFilterOptionAction() {
  408. if ( filterOptionAction == null ) {
  409. filterOptionAction = new FilterOptionAction();
  410. }
  411. return filterOptionAction;
  412. }
  413. private class FilterOptionAction implements ActionListener {
  414. public void actionPerformed( ActionEvent e ) {
  415. // whether or not to show fields, throws, and other visibility levels
  416. // just reparsing should do it
  417. if ( currentView != null )
  418. parse( currentView.getBuffer(), null );
  419. }
  420. }
  421. // controls how the visible items in the tree are displayed
  422. private ActionListener displayOptionAction = null;
  423. public ActionListener getDisplayOptionAction() {
  424. if ( displayOptionAction == null ) {
  425. displayOptionAction = new DisplayOptionAction();
  426. }
  427. return displayOptionAction;
  428. }
  429. private class DisplayOptionAction implements ActionListener {
  430. public void actionPerformed( ActionEvent e ) {
  431. // whether or not to show line numbers, arguments, etc
  432. // just reparsing should do it
  433. if ( currentView != null )
  434. parse( currentView.getBuffer(), null );
  435. }
  436. }
  437. public ActionListener getPropertySaveListener() {
  438. return new ActionListener() {
  439. public void actionPerformed( ActionEvent ae ) {
  440. if ( currentView != null )
  441. parse( currentView.getBuffer(), null );
  442. }
  443. };
  444. }
  445. /**
  446. * Returns if the parser supports code completion.
  447. *
  448. * Returns false by default.
  449. */
  450. public boolean supportsCompletion() {
  451. return true;
  452. }
  453. public SideKickCompletion complete( EditPane editPane, int caret ) {
  454. if ( completionFinder == null )
  455. completionFinder = new JavaCompletionFinder();
  456. return completionFinder.complete( editPane, caret );
  457. }
  458. }