View Javadoc

1   /*
2    * $Id: OWLAutoIndentStrategy.java,v 1.6 2005/07/11 20:31:09 jlerner Exp $
3    *
4    * Copyright (c) 1999-2004, BBN Technologies, LLC.
5    * All rights reserved.
6    * http://www.daml.org/legal/opensource/bbn_license.html
7    */
8    
9   package com.bbn.swede.editor.formatting;
10  
11  import java.util.StringTokenizer;
12  
13  import org.eclipse.jface.text.BadLocationException;
14  import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
15  import org.eclipse.jface.text.Document;
16  import org.eclipse.jface.text.DocumentCommand;
17  import org.eclipse.jface.text.IDocument;
18  import org.eclipse.jface.text.IRegion;
19  import org.eclipse.jface.text.ITypedRegion;
20  import org.eclipse.jface.text.TextUtilities;
21  
22  import com.bbn.swede.core.OWLCore;
23  import com.bbn.swede.editor.EditorPlugin;
24  import com.bbn.swede.editor.OWLPartitionScanner;
25  
26  
27  /***
28   * OWLAutoIndentStrategy extends DefaultAutoIndentStrategy to add an 
29   * indentation level for child tags and to remove an indention level for
30   * end tags.  This class also handles substitution of spaces for tabs if
31   * this setting is enabled in SWeDE editor preferences.
32   * @author jlerner
33   * @author rblace
34   */
35  public class OWLAutoIndentStrategy extends DefaultIndentLineAutoEditStrategy
36  {
37     /*
38      * (non-Javadoc)
39      * @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(
40      *       org.eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
41      */
42     public void customizeDocumentCommand(IDocument d, DocumentCommand c)
43     {
44        boolean isPaste = false;
45        if(c.text != null && c.text.length() > 2)
46        {
47           isPaste = true;
48        }
49        
50  //         super.customizeDocumentCommand(d, c);
51        
52        if(c.length == 0 && c.text != null && !isPaste)
53        {  
54           if (EditorPlugin.spacesForTabs() && c.text.indexOf("\t") >= 0)
55           {
56              substituteSpaces(c);
57           }
58           if(isLineDelimiter(d, c.text.substring(0, 1)))
59           {
60              handleNewLine(d, c);
61           }
62           else if(c.text.equals("/") || c.text.equals(">"))
63           {
64              handleCloseTag(d, c);
65           }
66        }
67        if(isPaste) 
68        {
69           handlePaste(d, c);
70        }
71        
72     }
73     
74     /***
75      * Replaces all tab characters in a document command with spaces.  The
76      * numbers of spaces substituted depends on the tab width stored in editor
77      * preferences.
78      * @param c The document command
79      */
80     private void substituteSpaces(DocumentCommand c)
81     {
82        String sTabText = FormattingUtils.getIndentSpaces(EditorPlugin.getTabWidth());
83        c.text = c.text.replaceAll("\t", sTabText);
84     }
85  
86     /***
87      * Called when a solo newline is typed into the document.  Will determine if any
88      * indenting or un-indenting needs to take place and will handle it.
89      * 
90      * @param d - IDocument
91      * @param c - DocumentCommand
92      */
93     private void handleNewLine(IDocument d, DocumentCommand c)
94     {
95        //this stuff will unindent for any end tags you might have right at the cursor
96        try
97        {            
98           //the region of the edit (previous line)
99           IRegion region = d.getLineInformationOfOffset(c.offset);
100          int lineOffset = region.getOffset();
101          int lineLength = region.getLength();
102          int iLine = d.getLineOfOffset(lineOffset);
103          
104          String currLine = d.get(lineOffset, lineLength);
105          String prevLine = currLine.substring(0, c.offset - lineOffset);
106          currLine = currLine.substring(c.offset - lineOffset);
107          
108          //if currLine is "</"
109          if(currLine.matches("^(//s)*<(//s)*/.*"))
110          {
111             //an end tag is at the start of this line
112             //we need to walk forward and grab the endline itself
113             int partIndex = c.offset;
114             while(d.getPartition(partIndex).getType() != OWLPartitionScanner.END_TAG)
115             {
116                partIndex++;
117             }
118             
119             //begin is the tag that matches the end tag that we just found
120             ITypedRegion begin = findMatchingBeginTag(d, d.getPartition(partIndex));
121             int indent = 0;
122             if(begin != null)
123             {
124                indent = FormattingUtils.getIndentLength(d, begin);            
125             }
126             else
127             {
128                trace("Couldnt find matching opening tag");
129             }
130             
131             //the new text is going to be the old newline plus the new indent
132             c.text = c.text.substring(0, 1) + FormattingUtils.getIndent(indent);
133             
134          }
135          else  //line doesnt contain "</", so check to see if we should indent ahead
136          {
137             region = findLastBeginTag(d, c.offset);
138             if (region != null)
139             {
140                iLine = d.getLineOfOffset(region.getOffset());
141                lineOffset = d.getLineOffset(iLine);
142                lineLength = d.getLineLength(iLine);
143                String sLine = d.get(lineOffset, lineLength);
144                int iIndent = FormattingUtils.getIndentLength(sLine);
145                
146                if (c.offset >= region.getOffset() 
147                    && c.offset < (region.getOffset() + region.getLength()))
148                {
149                   //autoindent for new attribute
150                   iIndent += FormattingUtils.getAttributeIndentSize();
151                }
152                else
153                {
154                   ITypedRegion regionEnd = findLastEndTag(d, c.offset);
155                   if (regionEnd == null || regionEnd.getOffset() < region.getOffset())
156                   {
157                      if (!d.get(region.getOffset(), region.getLength()).matches(FormattingUtils.SINGLETON_TAG_REGEX))
158                      {
159                         //between begin and end tags, add extra indentation
160                         iIndent += FormattingUtils.getTagIndentSize();
161                      }
162                      //else region specifies a singleton & iIndent is correct
163                   }
164                   else  //align with most recent end tag
165                   {
166                      iLine = d.getLineOfOffset(regionEnd.getOffset());
167                      sLine = d.get(d.getLineOffset(iLine), d.getLineLength(iLine));
168                      iIndent = FormattingUtils.getIndentLength(sLine);
169                   }
170                }
171                c.text = c.text + FormattingUtils.getIndent(iIndent);
172             }
173          }
174       }
175       catch(BadLocationException e)
176       {
177          return;
178       }
179    }
180 
181    /***
182     * When the user types "/" this function checks to see if it is part of a
183     * closing tag:"&lt;/".
184     * @param d The document being edited
185     * @param c The current document command
186     */
187    private void handleCloseTag(IDocument d, DocumentCommand c)
188    {                     
189       try
190       {            
191          IRegion region = d.getLineInformationOfOffset(c.offset);
192          int lineOffset = region.getOffset();
193          int lineLength = region.getLength();
194          
195          String currLine = d.get(lineOffset, lineLength);
196          int offsetIntoString = c.offset - lineOffset;
197          currLine = currLine.substring(0, offsetIntoString) + c.text 
198                     + currLine.substring(offsetIntoString);
199                      
200          if(currLine.matches("^(//s)*</.*"))
201          {
202             //make sure that what we're typing is at the start
203             if(currLine.indexOf("/") != offsetIntoString)
204             {
205                return;
206             }
207                
208             //its the start of a closing tag
209             alignWithOpenTag(d, d.getPartition(c.offset), c);
210          }
211          else if (currLine.matches("^(//s)*/?>.*"))
212          {
213             ITypedRegion rBegin = findLastBeginTag(d, c.offset);
214             if (rBegin != null && c.offset >= rBegin.getOffset()
215                 && c.offset < (rBegin.getOffset() + rBegin.getLength()))
216             {
217                int iLine = d.getLineOfOffset(rBegin.getOffset());
218                int iOffset = d.getLineOffset(iLine);
219                int iEnd = findEndOfWhiteSpace(d, iOffset, rBegin.getOffset());
220                int iIndent = iEnd - iOffset;
221                iIndent = FormattingUtils.getIndentLength(d.get(iOffset, iEnd - iOffset));
222                
223                IRegion lineRegion = d.getLineInformationOfOffset(c.offset);
224                String sWhitespace = FormattingUtils.getIndent(iIndent);
225                iEnd = findEndOfWhiteSpace(d, lineRegion.getOffset(), c.offset);
226                String sLineStart = d.get(lineRegion.getOffset(), iEnd - lineRegion.getOffset());
227                String sLineMid = d.get(iEnd, c.offset - iEnd);
228                c.offset -= sLineStart.length() + sLineMid.length();
229                c.length += sLineStart.length() + sLineMid.length();
230                c.text = sWhitespace + sLineMid + c.text;
231 
232             }
233          }
234          else
235          {
236             return;
237          }
238          
239       }
240       catch(BadLocationException e)
241       {
242          return;
243       }
244 
245    }
246 
247    /***
248     * Returns whether <code>text</code> is a valid line delimiter
249     * for <code>document</code>.
250     * 
251     * @param document document to be tested against
252     * @param text candidate line delimeter as a String
253     * @return true if a valid line delimiter for the given
254     *         <code>document</code>
255     */
256    private boolean isLineDelimiter(IDocument document, String text)
257    {
258       String[] delimiters = document.getLegalLineDelimiters();
259       if(delimiters != null)
260       {
261          return TextUtilities.equals(delimiters, text) > -1;
262       }
263       return false;
264    }
265 
266    /***
267     * Indents the DocumentCommand with its corresponding open tag
268     * in the ITypedRegion of the IDocument.
269     * @param d The document
270     * @param r The region
271     * @param c The command
272     */
273    private void alignWithOpenTag(IDocument d, ITypedRegion r,
274       DocumentCommand c)
275    {
276       ITypedRegion begin = findMatchingBeginTag(d, r);
277       int indent = 0;
278       if(begin != null)
279       {
280          indent = FormattingUtils.getIndentLength(d, begin);            
281       }
282       else
283       {
284          trace("Couldnt find the opening tag");
285       }
286       
287       // adjust current region to match previous open indent
288       try
289       {
290          IRegion lineRegion = d.getLineInformationOfOffset(c.offset);
291          String sWhitespace = FormattingUtils.getIndent(indent);
292          int iEnd = findEndOfWhiteSpace(d, lineRegion.getOffset(), c.offset);
293          String sLineStart = d.get(lineRegion.getOffset(), iEnd - lineRegion.getOffset());
294          String sLineMid = d.get(iEnd, c.offset - iEnd);
295          c.offset -= sLineStart.length() + sLineMid.length();
296          c.length += sLineStart.length() + sLineMid.length();
297          c.text = sWhitespace + sLineMid + c.text;
298          
299       }
300       catch (BadLocationException e)
301       {
302          OWLCore.logError(EditorPlugin.getID(),
303             "Unable to automatically modify document", e);
304       }
305    }
306 
307    /***
308     * Takes a <code>Document d</code> and aligns line <code>lineNumber</code> 
309     * with its matching opening tag.
310     * 
311     * @param d
312     * @param lineNumber
313     * @return modified <code>Document</code>
314     * @throws BadLocationException
315     */
316    private String alignWithOpenTag(Document d, int lineNumber) throws BadLocationException
317    {
318       int stack = 0;
319       stack--;
320       
321       IRegion origRegion = d.getLineInformation(lineNumber); 
322       IRegion region = null;         
323       String origLine = d.get(origRegion.getOffset(), origRegion.getLength());
324       String line = null;         
325       
326       while(stack < 0 && lineNumber > 0)
327       {
328          lineNumber--;
329          region = d.getLineInformation(lineNumber);
330          line = d.get(region.getOffset(), region.getLength());
331          
332          if(FormattingUtils.isBeginTag(line.trim()))
333          {
334             stack++;
335          }
336          else if(FormattingUtils.isEndTag(line.trim()))
337          {
338             stack--;
339          }
340       }
341       
342       int newIndent = FormattingUtils.getIndentLength(line);
343       int oldIndent = FormattingUtils.getIndentLength(origLine);
344       return FormattingUtils.getIndent(newIndent) + origLine.trim();
345 
346    }
347    
348    /***
349     * Returns the region of the matching begin tag for an ITypedRegion.
350     * @param d The document
351     * @param closeTag The region containing the close tag
352     * @return The region containing the begin tag matching
353     *         <code>closeTag</code>.
354     */
355    private ITypedRegion findMatchingBeginTag(IDocument d,
356       ITypedRegion closeTag)
357    {
358       return findBeginTag(d, closeTag);
359    }
360 
361    /***
362     * Returns the region of the begin tag for <code>ITypedRegion</code>
363     * that matches by number of open/closing tags.
364     * @param d The document
365     * @param closeTag The region containing the close tag
366     * @return The region containing the begin tag matching
367     *         <code>closeTag</code>.
368     */
369    private ITypedRegion findBeginTag(IDocument d, ITypedRegion closeTag)
370    {
371       if(closeTag == null)
372       {
373          return null;
374       }
375       
376       int stack = 0;
377       stack++;
378       
379       ITypedRegion current = closeTag;
380       while (stack != 0)
381       {
382          current = FormattingUtils.getPreviousPartition(d, current);
383          if(current == null)
384          {
385             return null;
386          }
387          
388          if(current.getType().equals(OWLPartitionScanner.BEGIN_TAG))
389          {
390             if(!isSingleton(d, current))
391             {
392                stack--;
393             }
394          }
395          else if(current.getType().equals(OWLPartitionScanner.END_TAG))
396          {
397             stack++;
398          }
399          
400       }
401       // current should now contain the open tag
402       return current;
403    }
404    
405    /***
406     * Finds the last begin tag before a specified offset in the document.
407     * If the offset is inside of a begin tag and is not the starting offset,
408     * that tag is returned.
409     * @param d The document
410     * @param iOffset The offset to start searching from
411     * @return The typed region of the first begin tag before or including 
412     *         <code>offset</code>, or <code>null</code> if no regions of
413     *         type BEGIN_TAG exist before <code>offset</code>.
414     * @throws BadLocationException
415     */
416    private ITypedRegion findLastBeginTag(IDocument d, int iOffset)
417       throws BadLocationException
418    {
419       ITypedRegion region = d.getPartition(iOffset);
420       while (region != null
421          && (!region.getType().equals(OWLPartitionScanner.BEGIN_TAG)
422             || iOffset == region.getOffset()))
423       {
424          region = FormattingUtils.getPreviousPartition(d, region);
425       }
426       return region;
427    }
428 
429    /***
430     * Finds the last end tag before a specified offset in the document.
431     * If the offset is inside of an end tag, that tag is returned.
432     * @param d The document
433     * @param iOffset The offset to start searching from
434     * @return The typed region of the first end tag before or including 
435     *         <code>offset</code>, or <code>null</code> if no regions of
436     *         type END_TAG exist before <code>offset</code>.
437     * @throws BadLocationException
438     */
439    private ITypedRegion findLastEndTag(IDocument d, int iOffset)
440       throws BadLocationException
441    {
442       ITypedRegion region = d.getPartition(iOffset);
443       while (region != null
444          && !region.getType().equals(OWLPartitionScanner.END_TAG))
445       {
446          region = FormattingUtils.getPreviousPartition(d, region);
447       }
448       return region;
449    }
450 
451    /***
452     * Indicates whether two tags match.
453     * @param d The document containing the tags
454     * @param openTag The open tag
455     * @param closeTag The close tag
456     * @return <code>true</code> if the tags match, <code>false</code> if not.
457     */
458    private boolean isMatchingTag(IDocument d,
459       ITypedRegion openTag, ITypedRegion closeTag)
460    {
461       if(!openTag.getType().equals(OWLPartitionScanner.BEGIN_TAG)
462          || !closeTag.getType().equals(OWLPartitionScanner.END_TAG))
463       {
464          return false;
465       }
466       String contentsOpen = "", contentsClose = "";
467       try
468       {
469          contentsOpen = d.get(openTag.getOffset() + 1,
470             openTag.getLength() - 2);
471          // trim off everything after first space
472          StringTokenizer st = new StringTokenizer(contentsOpen);
473          contentsOpen = st.nextToken();
474          contentsClose = d.get(closeTag.getOffset() + 2, closeTag.getLength() - 3);
475          // trim off everything after first space
476          st = new StringTokenizer(contentsClose);
477          contentsClose = st.nextToken();
478       }
479       catch (BadLocationException e)
480       {
481          OWLCore.logError(EditorPlugin.getID(),
482             "Unable to access document contents", e);
483          return false;
484       }
485       return contentsOpen.equals(contentsClose);
486    }
487 
488 
489 
490    /***
491     * Indicates whether a region in the document contains a singleton tag.
492     * @param d The document
493     * @param r The region
494     * @return <code>true</code> if <code>r</code> contains a singleton tag,
495     *         <code>false</code> if not.
496     */
497    private boolean isSingleton(IDocument d, ITypedRegion r)
498    {
499       boolean rv = false;
500       int slashLocation = r.getOffset() + r.getLength() - 2;
501       try
502       {
503          rv = (d.getChar(slashLocation) == '/');
504       }
505       catch (BadLocationException e)
506       {
507          OWLCore.logError(EditorPlugin.getID(),
508             "Unable to find location in document", e);
509       }
510       return rv;
511    }
512 
513    /***
514     * Handles the case where text is being pasted and needs to be properly
515     * indented.
516     * @param d IDocument being pasted into
517     * @param c DocumentCommand containing pasted text
518     */
519    private void handlePaste(IDocument d, DocumentCommand c)
520    {  
521       //if not a valid doc command, return
522       if(c.offset < 0 || d.getLength() == 0)
523       {
524          return;
525       }
526       
527       trace(c.text);
528       
529       Document tempDoc = new Document(c.text);            
530       boolean isValid = isValid(tempDoc);
531 
532       if(isValid)
533       {
534          trace("VALID");
535       }
536       else
537       {
538          trace("NOT VALID");
539       }
540       
541       int prevIndent = 0;
542       int firstLine = 0;
543       //p is equal to the paste position in the file
544       int p = (c.offset == d.getLength()) ? c.offset - 1 : c.offset;
545 
546       IRegion pasteRegion = null;
547       String pasteLine = null;
548       
549       try
550       {
551          //pasteLine is the paste line of the document (up to the start of the paste)
552          pasteRegion = d.getLineInformationOfOffset(p);
553          pasteLine = d.get(pasteRegion.getOffset(), c.offset - pasteRegion.getOffset());
554       }
555       catch(BadLocationException e)
556       {  
557          OWLCore.logError("OWLSourceViewerConfiguration", "Problem pasting text.", e);
558          return;
559       }
560          
561       trace("pasting to line: [" + pasteLine + "]");
562 
563       String prevLine = null; 
564       
565       //if there is nothing on the line before the paste
566       if(pasteLine.trim().length() == 0)
567       {
568          //add the whitespace from that line to the doc command
569          c.length += pasteLine.length();
570          c.offset = pasteRegion.getOffset();
571          firstLine = 0;
572          prevLine = getPreviousLine(d, c);
573          //prevIndent is the indent level of the previous line            
574          prevIndent = FormattingUtils.getIndentLength(prevLine);
575          trace("nothing on line, indent: " + prevIndent);
576       }
577       else
578       {
579          //indentLevel is the indent level of this line
580          firstLine = 1;    
581          prevLine = getPreviousLine(d, c);
582          //prevIndent is the indent level of the previous line            
583          prevIndent = FormattingUtils.getIndentLength(prevLine);
584          trace("stuff on line, indent: " + prevIndent);
585       }
586       
587       IRegion currRegion = null;
588       String currLine = null;
589       int indentChange = 0;
590       
591       for(int i = firstLine; i < tempDoc.getNumberOfLines(); i++)
592       {
593          try
594          {
595             trace("LINE " + i);
596             currRegion = tempDoc.getLineInformation(i);
597             currLine = tempDoc.get(currRegion.getOffset(), currRegion.getLength());
598             trace("currLine: " + currLine);
599             
600             if(currLine.trim().length() == 0)
601             {
602                continue;
603             }
604             
605             if(isValid)
606             {
607                //reformat completely                 
608                if(FormattingUtils.isEndTag(currLine.trim()))
609                {
610                   //its an end tag, so we want to indent to same level as open tag
611                   currLine = alignWithOpenTag(tempDoc, i);
612                   tempDoc.replace(currRegion.getOffset(), currRegion.getLength(), currLine);
613                   trace("[curr:END] replacement: " + currLine + " [" + FormattingUtils.getIndentLength(currLine) + "]");
614                   prevLine = currLine;
615                }
616                else 
617                {
618                   //its either a begin tag or a comment tag or whatever
619                   if(FormattingUtils.isBeginTag(prevLine.trim()))
620                   {
621                      //the last one was a beginTag, indent more
622                      int newIndent = FormattingUtils.getIndentLength(prevLine) + FormattingUtils.getTagIndentSize();
623                      int oldIndent = FormattingUtils.getIndentLength(currLine);
624                      currLine = FormattingUtils.getIndent(newIndent) + currLine.trim();
625                      tempDoc.replace(currRegion.getOffset(), currRegion.getLength(), currLine);
626                      trace("[prev:BEGIN] newIndent:" + newIndent + " oldIndent:" + oldIndent);
627                      trace("[curr:OTHER] replacement: " + currLine + " ["
628                         + FormattingUtils.getIndentLength(currLine) + "]");
629                      prevLine = currLine;
630                   }
631                   else
632                   {
633                      //just go with the indent of the last line
634                      int newIndent = FormattingUtils.getIndentLength(prevLine);
635                      int oldIndent = FormattingUtils.getIndentLength(currLine);
636                      currLine = FormattingUtils.getIndent(newIndent) + currLine.trim();
637                      tempDoc.replace(currRegion.getOffset(), currRegion.getLength(), currLine);
638                      trace("[prev:OTHER] newIndent:" + newIndent + " oldIndent:" + oldIndent);
639                      trace("[curr:END] replacement: " + currLine + " ["
640                         + FormattingUtils.getIndentLength(currLine) + "]");
641                      prevLine = currLine;
642                   }
643                }
644             
645             }
646             //else...nevermind.  Things are too broken to bother autoindenting.
647          }
648          catch(BadLocationException e)
649          {
650             trace("Error processing line " + i + " of paste");
651          }
652       }      
653       
654       trace("post all this stuff yo: \n" + tempDoc.get());
655       c.text = tempDoc.get();
656       c.caretOffset = c.offset + c.text.length();
657       c.shiftsCaret = false;
658       
659    }
660 
661    private String getPreviousLine(IDocument d, DocumentCommand c)
662    {
663       String retBeginTag = "< >";
664       String retEndTag = "</ >";
665       
666       try
667       {
668          int offset = c.offset - 1;
669          ITypedRegion typedRegion = d.getPartition(offset);
670          
671          while(typedRegion.getType() != OWLPartitionScanner.BEGIN_TAG
672              && typedRegion.getType() != OWLPartitionScanner.END_TAG)
673          {
674             typedRegion = FormattingUtils.getPreviousPartition(d, typedRegion);
675          }
676          
677          //now we have a partition we can work with
678          IRegion tRegion = d.getLineInformationOfOffset(typedRegion.getOffset());
679          String tString = d.get(tRegion.getOffset(), tRegion.getLength());
680          int tIndent = FormattingUtils.getIndentLength(tString);
681          
682          if(typedRegion.getType() == OWLPartitionScanner.BEGIN_TAG)
683          {
684             if(isSingleton(d, typedRegion))
685             {
686                return FormattingUtils.getIndent(tIndent) + retEndTag;
687             }
688             else
689             {
690                return FormattingUtils.getIndent(tIndent) + retBeginTag;
691             }
692          }
693          else
694          {
695             return FormattingUtils.getIndent(tIndent) + retEndTag;
696          }
697       }
698       catch(BadLocationException e)
699       {
700          trace("BadLocation Error");
701          return retEndTag;
702       }
703       catch(NullPointerException e)
704       {
705          trace("NullPointerException");
706          return retEndTag;
707       }
708       
709    }
710 
711    /***
712     * Determines whether the document is very basically correct.  This means
713     * an equal number of open/close tags and they are ordered correctly.
714     * @param d The document
715     * @return <code>true</code> if the tags match up, <code>false</code> if not.
716     */
717    private boolean isValid(Document d)
718    {  
719       boolean retVal = false;
720       String line = null;
721       IRegion region = null;
722 
723       int stack = 0;
724       for(int i = 0; i < d.getNumberOfLines(); i++)
725       {
726          try
727          {
728             region = d.getLineInformation(i);
729             line = d.get(region.getOffset(), region.getLength()).trim();
730          }
731          catch (BadLocationException e)
732          {
733             return false;
734          }
735 
736          if(FormattingUtils.isBeginTag(line))
737          {
738             stack++;
739          }
740          else if(FormattingUtils.isEndTag(line))
741          {
742             stack--;
743          }
744             
745          if(stack < 0)
746          {
747             return false;
748          }
749       }
750       
751       return true;
752    }
753    
754    private void trace(String msg)
755    {
756       if(false)
757       {
758          trace(msg, false);
759       }
760    }
761 
762    private void trace(String msg, boolean error)
763    {
764       OWLCore.trace("OWLSourceViewerConfiguration", msg, error);
765    }   
766 }