Compilation
[scilab.git] / scilab / modules / scinotes / src / java / org / scilab / modules / scinotes / ScilabDocument.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - Calixte DENIZET
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10  *
11  */
12
13 package org.scilab.modules.scinotes;
14
15 import java.nio.charset.Charset;
16
17 import java.util.Vector;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.TreeSet;
21 import java.util.Set;
22 import java.util.HashSet;
23 import java.util.Comparator;
24 import java.util.Iterator;
25
26 import javax.swing.text.GapContent;
27 import javax.swing.text.PlainDocument;
28 import javax.swing.text.BadLocationException;
29 import javax.swing.text.Element;
30 import javax.swing.text.AttributeSet;
31 import javax.swing.text.View;
32
33 import javax.swing.tree.DefaultMutableTreeNode;
34
35 import javax.swing.event.DocumentEvent;
36 import javax.swing.event.DocumentListener;
37
38 import org.scilab.modules.scinotes.utils.ConfigSciNotesManager;
39 import org.scilab.modules.scinotes.utils.SciNotesMessages;
40 import org.scilab.modules.console.utils.ScilabLaTeXViewer;
41
42 /**
43  * The class ScilabDocument is used to render a document .sci or .sce
44  * @author Calixte DENIZET
45  */
46 public class ScilabDocument extends PlainDocument implements DocumentListener {
47
48     private static final long serialVersionUID = -1227880612912063687L;
49
50     /**
51      * The EOL in mac OS
52      */
53     public static final String EOLMAC = "\r";
54
55     /**
56      * The EOL in windows OS
57      */
58     public static final String EOLWIN = "\r\n";
59
60     /**
61      * The EOL in unix OS
62      */
63     public static final String EOLUNIX = "\n";
64
65     private static final int GAPBUFFERCAPACITY = 2;
66     private static final String LINE_SEPARATOR = "line.separator";
67     private static final int INITFUNCTIONSNUMBER = 128;
68
69     private View view;
70     private List<String> saved = new Vector<String>();
71     private FunctionScanner funScanner;
72
73     private Set<String> functions = new HashSet<String>(INITFUNCTIONSNUMBER);
74
75     private boolean contentModified;
76     private boolean contentModifiedSinceBackup;
77     private boolean alphaOrder;
78
79     // Editor's default encoding is UTF-8
80     private String encoding;
81     private boolean updater = true;
82     private boolean binary;
83     private boolean autoIndent;
84     private boolean shouldMergeEdits;
85     private boolean undoManagerEnabled;
86     private CompoundUndoManager undo;
87
88     private ScilabEditorPane pane;
89     private boolean focused;
90
91     private String eolStyle;
92
93     /**
94      * Constructor
95      */
96     public ScilabDocument() {
97         this(true);
98     }
99
100     /**
101      * Constructor
102      */
103     public ScilabDocument(boolean paned) {
104         super(new GapContent(GAPBUFFERCAPACITY));
105         contentModified = false;
106
107         if (paned) {
108             setAsynchronousLoadPriority(2);
109
110             autoIndent = SciNotesOptions.getSciNotesDisplay().automaticIndent;
111             encoding = Charset.forName(SciNotesOptions.getSciNotesPreferences().encoding).toString();
112             eolStyle = SciNotesOptions.getSciNotesPreferences().eol;
113
114             undo = new CompoundUndoManager(this);
115             addUndoableEditListener(undo);
116             undoManagerEnabled = true;
117
118             contentModifiedSinceBackup = false;
119         }
120     }
121
122     /**
123      * Set the ScilabEditorPane associated with this doc
124      * @param pane the ScilabEditorPane
125      */
126     public void setEditorPane(ScilabEditorPane pane) {
127         this.pane = pane;
128     }
129
130     /**
131      * Get the ScilabEditorPane associated with this doc
132      * @return pane the ScilabEditorPane
133      */
134     public ScilabEditorPane getEditorPane() {
135         return pane;
136     }
137
138     /**
139      * Set to true of the document is focused in the EditorPane
140      * @param b the boolean
141      */
142     public void setFocused(boolean b) {
143         focused = b;
144     }
145
146     /**
147      * Create a lexer used to colorize the text
148      * @return ScilabLexer the lexer
149      */
150     public ScilabLexer createLexer() {
151         return new ScilabLexer(this);
152     }
153
154     /**
155      * @return the Set containing the functions name
156      */
157     public Set<String> getFunctionsInDoc() {
158         return functions;
159     }
160
161     /**
162      * Set the current view to render the code
163      * @param view the used view
164      */
165     public void setView(View view) {
166         this.view = view;
167     }
168
169     /**
170      * @return the current used view
171      */
172     public View getView() {
173         return view;
174     }
175
176     /**
177      * Get encoding
178      * @return String encoding
179      */
180     public String getEncoding() {
181         return encoding;
182     }
183
184     /**
185      * set Encoding
186      * @param encoding encoding
187      */
188     public void setEncoding(String encoding) {
189         this.encoding = encoding;
190     }
191
192     /**
193      * Set to true if it's a binary doc
194      * @param binary boolean
195      */
196     public void setBinary(boolean binary) {
197         this.binary = binary;
198     }
199
200     /**
201      * @return true if it's a binary file
202      */
203     public boolean getBinary() {
204         return binary;
205     }
206
207     /**
208      * set end of line value
209      * @param eol String
210      */
211     public void setEOL(String eol) {
212         this.eolStyle = eol;
213     }
214
215     /**
216      * get end of line
217      * @return end of line
218      */
219     public String getEOL() {
220         return this.eolStyle;
221     }
222
223     /**
224      * get end of line
225      * @return end of line
226      */
227     public String getDefaultEOL() {
228         return System.getProperty(LINE_SEPARATOR);
229     }
230
231     /**
232      * isUpdater
233      * @return boolean
234      */
235     public boolean isUpdater() {
236         return updater;
237     }
238
239     /**
240      * getAutoIndent
241      * @return boolean
242      */
243     public boolean getAutoIndent() {
244         return autoIndent;
245     }
246
247     /**
248      * setAutoIndent
249      * @param b boolean
250      */
251     public void setAutoIndent(boolean b) {
252         autoIndent = b;
253     }
254
255     /**
256      * setUpdater
257      * @param updaterDisabled boolean
258      */
259     public void setUpdater(boolean updaterDisabled) {
260         this.updater = updaterDisabled;
261     }
262
263     /**
264      * Get document text
265      * @return String
266      */
267     public String getText() {
268         try {
269             return getText(0, getLength());
270         } catch (BadLocationException e) {
271             return "";
272         }
273     }
274
275     public void addEOL() {
276         if (SciNotesOptions.getSciNotesPreferences().addLineTermination) {
277             int len = getLength();
278             int lenEOL = getEOL().length();
279             if (getLength() >= lenEOL) {
280                 try {
281                     String end = getText(len - lenEOL, lenEOL);
282                     if (!end.equals(getEOL())) {
283                         insertString(len, getEOL(), null);
284                     }
285                 } catch (BadLocationException e) {
286                     System.err.println(e);
287                 }
288             } else {
289                 try {
290                     insertString(len, getEOL(), null);
291                 } catch (BadLocationException e) {
292                     System.err.println(e);
293                 }
294             }
295         }
296     }
297
298     /**
299      * Begins a compound edit (for the undo)
300      */
301     public void mergeEditsBegin() {
302         undo.endCompoundEdit();
303         undo.startCompoundEdit();
304     }
305
306     /**
307      * Ends a compound edit (for the undo)
308      */
309     public void mergeEditsEnd() {
310         undo.endCompoundEdit();
311     }
312
313     /**
314      * getUndoManager
315      * @return CompoundUndoManager
316      */
317     public CompoundUndoManager getUndoManager() {
318         return undo;
319     }
320
321     /**
322      * disableUndoManager
323      */
324     public void disableUndoManager() {
325         if (undoManagerEnabled) {
326             this.removeUndoableEditListener(undo);
327             undoManagerEnabled = false;
328         }
329     }
330
331     /**
332      * enableUndoManager
333      */
334     public void enableUndoManager() {
335         if (!undoManagerEnabled) {
336             this.addUndoableEditListener(undo);
337             undoManagerEnabled = true;
338         }
339     }
340
341     /**
342      * isContentModified
343      * @return boolean
344      */
345     public boolean isContentModified() {
346         return contentModified;
347     }
348
349     /**
350      * isContentModifiedSinceBackup
351      * @return boolean
352      */
353     public boolean isContentModifiedSinceBackup() {
354         return contentModifiedSinceBackup;
355     }
356
357     /**
358      * setContentModified
359      * @param contentModified boolean
360      */
361     public void setContentModifiedSinceBackup(boolean contentModified) {
362         this.contentModifiedSinceBackup = contentModified;
363     }
364
365     /**
366      * setContentModified
367      * @param contentModified boolean
368      */
369     public void setContentModified(boolean contentModified) {
370         this.contentModified = contentModified;
371         if (pane != null && !contentModified) {
372             undo.setReference();
373             pane.updateTitle();
374         } else {
375             this.contentModifiedSinceBackup = true;
376         }
377     }
378
379     /**
380      * dump document on stderr with line positions
381      */
382     public void dump() {
383         readLock();
384         try {
385             Element root = getDefaultRootElement();
386             for (int i = 0; i != root.getElementCount(); ++i) {
387                 Element e = root.getElement(i);
388                 int start = e.getStartOffset();
389                 int end = e.getEndOffset();
390                 System.err.println("line " + i + " from: " + start + " to: " + end + ":|" + getText(start, end - start) + "|");
391             }
392         } catch (BadLocationException e) {
393             System.err.println(e);
394         }
395         readUnlock();
396     }
397
398     /**
399      * Search the position of the function name in the Document
400      * @param name the name of the function
401      * @return the position where to go or -1 if not found
402      */
403     public int searchFunctionByName(String name) {
404         Element root = getDefaultRootElement();
405         for (int i = 0; i < root.getElementCount(); i++) {
406             Element e = root.getElement(i);
407             if (e instanceof ScilabLeafElement) {
408                 ScilabLeafElement se = (ScilabLeafElement) e;
409                 if (se.isFunction() && se.getFunctionInfo().functionName.equals(name)) {
410                     return e.getStartOffset();
411                 }
412             }
413         }
414         return -1;
415     }
416
417     /**
418      * @return a list containing all the infos about functions available in this document
419      */
420     public List<FunctionScanner.FunctionInfo> getFunctionInfo() {
421         List<FunctionScanner.FunctionInfo> list = new ArrayList<FunctionScanner.FunctionInfo>();
422         Element root = getDefaultRootElement();
423         for (int i = 0; i < root.getElementCount(); i++) {
424             Element e = root.getElement(i);
425             if (e instanceof ScilabLeafElement) {
426                 ScilabLeafElement se = (ScilabLeafElement) e;
427                 if (se.isFunction()) {
428                     list.add(se.getFunctionInfo());
429                 }
430             }
431         }
432         return list;
433     }
434
435     /**
436      * @param alphaOrder is true if names must be sorted with alphabetic order
437      */
438     public void setAlphaOrderInTree(boolean alphaOrder) {
439         this.alphaOrder = alphaOrder;
440     }
441
442     /**
443      * Fill a tree with function's name according to alphabetic order or not
444      * @param base to fill
445      */
446     public synchronized void fillTreeFuns(DefaultMutableTreeNode base) {
447         Element root = getDefaultRootElement();
448         int nlines = root.getElementCount();
449         if (!alphaOrder) {
450             for (int i = 0; i < nlines; i++) {
451                 Element elem = root.getElement(i);
452                 if (elem instanceof ScilabDocument.ScilabLeafElement) {
453                     int type = ((ScilabDocument.ScilabLeafElement) elem).getType();
454                     switch (type) {
455                         case ScilabDocument.ScilabLeafElement.NOTHING :
456                             break;
457                         case ScilabDocument.ScilabLeafElement.FUN :
458                             base.add(new DefaultMutableTreeNode(elem));
459                             break;
460                         case ScilabDocument.ScilabLeafElement.ENDFUN :
461                             break;
462                         default :
463                             break;
464                     }
465                 }
466             }
467         } else {
468             Set<DefaultMutableTreeNode> set = new TreeSet<DefaultMutableTreeNode>(new Comparator<DefaultMutableTreeNode>() {
469                 public int compare(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
470                     ScilabLeafElement l1 = (ScilabLeafElement) o1.getUserObject();
471                     ScilabLeafElement l2 = (ScilabLeafElement) o2.getUserObject();
472                     int n = l1.getFunctionName().compareTo(l2.getFunctionName());
473                     if (n != 0) {
474                         return n;
475                     }
476                     return l1.getStartOffset() - l2.getStartOffset();
477                 }
478
479                 public boolean equals(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
480                     return false;
481                 }
482             });
483             for (int i = 0; i < nlines; i++) {
484                 Element elem = root.getElement(i);
485                 if (elem instanceof ScilabDocument.ScilabLeafElement) {
486                     int type = ((ScilabDocument.ScilabLeafElement) elem).getType();
487                     switch (type) {
488                         case ScilabDocument.ScilabLeafElement.NOTHING :
489                             break;
490                         case ScilabDocument.ScilabLeafElement.FUN :
491                             set.add(new DefaultMutableTreeNode(elem));
492                             break;
493                         case ScilabDocument.ScilabLeafElement.ENDFUN :
494                             break;
495                         default :
496                             break;
497                     }
498                 }
499             }
500             Iterator<DefaultMutableTreeNode> iter = set.iterator();
501             while (iter.hasNext()) {
502                 base.add(iter.next());
503             }
504         }
505     }
506
507     /**
508      * Fill a tree with anchor's name according to alphabetic order or not
509      * @param base to fill
510      */
511     public synchronized void fillTreeAnchors(DefaultMutableTreeNode base) {
512         Element root = getDefaultRootElement();
513         int nlines = root.getElementCount();
514         if (!alphaOrder) {
515             for (int i = 0; i < nlines; i++) {
516                 ScilabLeafElement elem = (ScilabLeafElement) root.getElement(i);
517                 if (elem.isAnchor()) {
518                     base.add(new DefaultMutableTreeNode(elem));
519                 }
520             }
521         } else {
522             Set<DefaultMutableTreeNode> set = new TreeSet<DefaultMutableTreeNode>(new Comparator<DefaultMutableTreeNode>() {
523                 public int compare(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
524                     ScilabLeafElement l1 = (ScilabLeafElement) o1.getUserObject();
525                     ScilabLeafElement l2 = (ScilabLeafElement) o2.getUserObject();
526                     int n = l1.getAnchorName().compareTo(l2.getAnchorName());
527                     if (n != 0) {
528                         return n;
529                     }
530                     return l1.getStartOffset() - l2.getStartOffset();
531                 }
532
533                 public boolean equals(DefaultMutableTreeNode o1, DefaultMutableTreeNode o2) {
534                     return false;
535                 }
536             });
537             for (int i = 0; i < nlines; i++) {
538                 ScilabLeafElement elem = (ScilabLeafElement) root.getElement(i);
539                 if (elem.isAnchor()) {
540                     set.add(new DefaultMutableTreeNode(elem));
541                 }
542             }
543             Iterator<DefaultMutableTreeNode> iter = set.iterator();
544             while (iter.hasNext()) {
545                 base.add(iter.next());
546             }
547         }
548     }
549
550     /**
551      * @return the first function name which appears in this doc or null
552      */
553     public String getFirstFunctionName() {
554         Element root = getDefaultRootElement();
555         for (int i = 0; i < root.getElementCount(); i++) {
556             Element e = root.getElement(i);
557             if (e instanceof ScilabLeafElement) {
558                 ScilabLeafElement se = (ScilabLeafElement) e;
559                 if (se.isFunction()) {
560                     return se.getFunctionInfo().functionName;
561                 }
562             }
563         }
564         return null;
565     }
566
567     /**
568      * @param line the number of the line where to begin the search
569      * @return the next anchor
570      */
571     public int nextAnchorFrom(int line) {
572         Element root = getDefaultRootElement();
573         for (int i = line + 1; i < root.getElementCount(); i++) {
574             ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
575             if (se.isAnchor()) {
576                 return i;
577             }
578         }
579         for (int i = 0; i < line; i++) {
580             ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
581             if (se.isAnchor()) {
582                 return i;
583             }
584         }
585
586         return -1;
587     }
588
589     /**
590      * @param line the number of the line where to begin the search
591      * @return the previous anchor
592      */
593     public int previousAnchorFrom(int line) {
594         Element root = getDefaultRootElement();
595         for (int i = line - 1; i >= 0; i--) {
596             ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
597             if (se.isAnchor()) {
598                 return i;
599             }
600         }
601         for (int i = root.getElementCount() - 1; i > line; i--) {
602             ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
603             if (se.isAnchor()) {
604                 return i;
605             }
606         }
607
608         return -1;
609     }
610
611     /**
612      * Get the anchors between two positions
613      * @param start the beginning
614      * @param end the end
615      * @return a list of the anchors
616      */
617     public List<Anchor> getAnchorsBetween(int start, int end) {
618         Element root = getDefaultRootElement();
619         int lineS = root.getElementIndex(start);
620         int lineE = root.getElementIndex(end);
621         List<Anchor> list = new ArrayList<Anchor>();
622         for (int i = lineS; i <= lineE; i++) {
623             final ScilabLeafElement se = (ScilabLeafElement) root.getElement(i);
624             if (se.isAnchor()) {
625                 list.add(new Anchor(i, se.getAnchorName()));
626             }
627         }
628
629         return list;
630     }
631
632     /**
633      * Get the lhs/rhs args used in a function declaration
634      * @param pos the position in the document
635      * @return the two lists containing args and returned values or null if we are not
636      * in a function
637      */
638     public List<String>[] getInOutArgs(int pos) {
639         Element root = getDefaultRootElement();
640         int index = root.getElementIndex(pos);
641         int compt = 0;
642         while (index != -1) {
643             ScilabLeafElement e = (ScilabLeafElement) root.getElement(index--);
644             switch (e.getType()) {
645                 case ScilabLeafElement.NOTHING :
646                     break;
647                 case ScilabLeafElement.FUN :
648                     if (compt == 0) {
649                         FunctionScanner.FunctionInfo info = e.getFunctionInfo();
650                         return new List[] {info.returnValues, info.argsValues};
651                     } else {
652                         compt++;
653                     }
654                     break;
655                 case ScilabLeafElement.ENDFUN :
656                     compt--;
657                     break;
658                 default :
659             }
660         }
661         return null;
662     }
663
664     /**
665      * Get the function name where the caret is
666      * @param pos the position in the document
667      * @return the nearest function name
668      */
669     public String getCurrentFunction(int pos) {
670         Element root = getDefaultRootElement();
671         int index = root.getElementIndex(pos);
672         int line = index;
673         int compt = 0;
674         while (index != -1) {
675             ScilabLeafElement e = (ScilabLeafElement) root.getElement(index--);
676             switch (e.getType()) {
677                 case ScilabLeafElement.NOTHING :
678                     break;
679                 case ScilabLeafElement.FUN :
680                     if (compt == 0) {
681                         String str = e.getFunctionInfo().functionName;
682                         if (str == null) {
683                             str = SciNotesMessages.UNKNOWN_FUNCTION;
684                         }
685                         return String.format(SciNotesMessages.POSFUN_IN_DOC, line + 1, pos - root.getElement(line).getStartOffset(), str, line - index);
686                     } else {
687                         compt++;
688                     }
689                     break;
690                 case ScilabLeafElement.ENDFUN :
691                     compt--;
692                     break;
693                 default :
694             }
695         }
696         return String.format(SciNotesMessages.POS_IN_DOC, line + 1, pos - root.getElement(line).getStartOffset());
697     }
698
699     /**
700      * lock
701      */
702     public void lock() {
703         super.writeLock();
704     }
705
706     /**
707      * unlock
708      */
709     public void unlock() {
710         super.writeUnlock();
711     }
712
713     /**
714      * Nothing !
715      * @param e the event
716      */
717     public void changedUpdate(DocumentEvent e) { }
718
719     /**
720      * Called when an insertion is made in the doc
721      * @param e the event
722      */
723     public void insertUpdate(DocumentEvent e) {
724         handleEvent(e);
725     }
726
727     /**
728      * Called when a remove is made in the doc
729      * @param e the event
730      */
731     public void removeUpdate(DocumentEvent e) {
732         handleEvent(e);
733     }
734
735     /**
736      * {@inheritDoc}
737      */
738     protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
739         // Fix bug 8277 in putting attr=null
740         // Java, by default, highlights the chinese chars when entered on keyboard
741         super.insertUpdate(chng, null);
742     }
743
744     /**
745      * @param ev the DocumentEvent to handle
746      */
747     private void handleEvent(DocumentEvent ev) {
748         if (!contentModified && pane != null) {
749             contentModified = true;
750             pane.updateTitle();
751         }
752         contentModifiedSinceBackup = true;
753
754         DocumentEvent.ElementChange chg = ev.getChange(getDefaultRootElement());
755         if (chg != null) {
756             Element[] added = chg.getChildrenAdded();
757             Element[] removed = chg.getChildrenRemoved();
758             if ((added != null && added.length > 0) || (removed != null && removed.length > 0)) {
759                 for (int i = 0; i < removed.length; i++) {
760                     String name = ((ScilabLeafElement) removed[i]).getFunctionName();
761                     if (name != null && name.length() != 0) {
762                         functions.remove(name);
763                     }
764                 }
765                 for (int i = 0; i < added.length; i++) {
766                     ((ScilabLeafElement) added[i]).resetType();
767                     ((ScilabLeafElement) added[i]).resetTypeWhenBroken();
768                     String name = ((ScilabLeafElement) added[i]).getFunctionName();
769                     if (name != null && name.length() != 0) {
770                         functions.add(name);
771                     }
772                 }
773             }
774         } else {
775             // change occurred only on one line
776             Element root = getDefaultRootElement();
777             int index = root.getElementIndex(ev.getOffset());
778             ScilabLeafElement line = (ScilabLeafElement) root.getElement(index);
779             boolean broken = line.isBroken();
780             if (pane != null && (line.resetType() == ScilabLeafElement.FUN || broken != line.isBroken()
781                                  || (index > 0 && ((ScilabLeafElement) root.getElement(index - 1)).isBroken()))) {
782                 pane.repaint();
783             }
784         }
785
786         if (pane != null) {
787             KeywordEvent e = pane.getKeywordEvent();
788
789             if (ScilabLexerConstants.isLaTeX(e.getType())) {
790                 try {
791                     int start = e.getStart();
792                     int end = start + e.getLength();
793                     String exp = getText(start, e.getLength());
794                     int height = pane.getScrollPane().getHeight() + pane.getScrollPane().getVerticalScrollBar().getValue();
795                     ScilabLaTeXViewer.displayExpressionIfVisible(pane, height, exp, start, end);
796                 } catch (BadLocationException ex) { }
797             }
798         }
799     }
800
801     /**
802      * @overload #createDefaultRoot
803      * @return the element base
804      */
805     protected AbstractElement createDefaultRoot() {
806         funScanner = new FunctionScanner(this);
807         BranchElement map = (BranchElement) createBranchElement(null, null);
808         Element line = createLeafElement(map, null, 0, 1);
809         map.replace(0, 0, new Element[] {line});
810         return map;
811     }
812
813     /**
814      * @overload #createLeafElement
815      * @param parent the parent Element
816      * @param a an AttributeSet
817      * @param p0 start in the doc
818      * @param p1 end in the doc
819      * @return the created LeafElement
820      */
821     protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) {
822         return new ScilabLeafElement(parent, a, p0, p1);
823     }
824
825     /**
826      * Inner class to consider the type of a line :
827      *  - FUN : function y=foo(x)
828      *  - ENDFUN : endfunction
829      *  - NOTHING : bla bla bla
830      * This inner class is useful to make a line numbering compatible with the whereami macro.
831      */
832     public class ScilabLeafElement extends LeafElement {
833
834         private static final long serialVersionUID = 4389590345677765643L;
835
836         /**
837          * Nothing in this line
838          */
839         public static final int NOTHING = 0;
840
841         /**
842          * function ... in this line
843          */
844         public static final int FUN = 1;
845
846         /**
847          * endfunction in this line
848          */
849         public static final int ENDFUN = 2;
850
851         /**
852          * broken line
853          */
854         public static final int BROKEN = 4;
855
856         private boolean visible = true;
857         private int type;
858         private FunctionScanner.FunctionInfo info;
859         private boolean broken;
860         private boolean brokenString;
861
862         private boolean anchor;
863         private String anchorName;
864
865         /**
866          * The same constructor as in LeafElement.
867          * @param parent the parent Element
868          * @param a an AttributeSet
869          * @param p0 start in the doc
870          * @param p1 end in the doc
871          */
872         public ScilabLeafElement(Element parent, AttributeSet a, int p0, int p1) {
873             super(parent, a, p0, p1);
874             type = funScanner.getLineType(p0, p1);
875             if ((type & BROKEN) == BROKEN) {
876                 broken = true;
877                 type -= BROKEN;
878             }
879
880             if (type == FUN) {
881                 info = funScanner.getFunctionInfo();
882             }
883         }
884
885         /**
886          * Reset type (normally called on a change in the document)
887          * @return the new type
888          */
889         public int resetType() {
890             String oldName = "";
891             if (type == FUN) {
892                 oldName = info.functionName;
893             }
894
895             type = funScanner.getLineType(getStartOffset(), getEndOffset());
896
897             if ((type & BROKEN) == BROKEN) {
898                 broken = true;
899                 type -= BROKEN;
900             } else {
901                 broken = false;
902             }
903
904             if (type == FUN) {
905                 info = funScanner.getFunctionInfo();
906                 if (info.functionName != null) {
907                     if (!info.functionName.equals(oldName)) {
908                         functions.remove(oldName);
909                         functions.add(info.functionName);
910                     }
911                 } else {
912                     functions.remove(oldName);
913                 }
914             }
915
916             resetTypeWhenBroken();
917
918             return type;
919         }
920
921         /**
922          * If the previous line is broken, then this line is a part of it
923          * so we need to resetType of the previous.
924          */
925         public void resetTypeWhenBroken() {
926             int p0 = getStartOffset();
927             if (p0 != 0) {
928                 Element parent = getParentElement();
929                 ScilabLeafElement elem = (ScilabLeafElement) parent.getElement(parent.getElementIndex(p0 - 1));
930                 if (elem.broken) {
931                     elem.resetType();
932                 }
933             }
934         }
935
936         /**
937          * @return the type of this line (FUN,...)
938          */
939         public int getType() {
940             return type;
941         }
942
943         /**
944          * @return the info about this line containing a function def
945          */
946         public FunctionScanner.FunctionInfo getFunctionInfo() {
947             return info;
948         }
949
950         /**
951          * @return if this line begins with function
952          */
953         public boolean isFunction() {
954             return type == FUN;
955         }
956
957         /**
958          * @return if this line begins with endfunction
959          */
960         public boolean isEndfunction() {
961             return type == ENDFUN;
962         }
963
964         /**
965          * @return if this line is visible
966          */
967         public boolean isVisible() {
968             return visible;
969         }
970
971         /**
972          * @param b true if this line is visible
973          */
974         public void setVisible(boolean b) {
975             visible = b;
976         }
977
978         /**
979          * @return if this line is broken
980          */
981         public boolean isBroken() {
982             return broken;
983         }
984
985         /**
986          * @param b true if this line is broken
987          */
988         public void setBroken(boolean b) {
989             broken = b;
990         }
991
992         /**
993          * @return if this line is broken
994          */
995         public boolean isBrokenString() {
996             return brokenString;
997         }
998
999         /**
1000          * @param b true if this line is broken in a string
1001          */
1002         public void setBrokenString(boolean b) {
1003             brokenString = b;
1004             if (b) {
1005                 broken = true;
1006             }
1007         }
1008
1009         /**
1010          * @return the function's name
1011          */
1012         public String getFunctionName() {
1013             if (type == FUN) {
1014                 return info.functionName;
1015             }
1016             return "";
1017         }
1018
1019         /**
1020          * @return if this line is an anchor
1021          */
1022         public boolean isAnchor() {
1023             return anchor;
1024         }
1025
1026         /**
1027          * @param name the name of the anchor, if null remove
1028          * the anchor.
1029          */
1030         public void setAnchor(String name) {
1031             if (name == null) {
1032                 anchor = false;
1033                 return;
1034             }
1035
1036             anchor = true;
1037             anchorName = name;
1038         }
1039
1040         /**
1041          * @return the name of the anchor if exists
1042          */
1043         public String getAnchorName() {
1044             if (anchor) {
1045                 return anchorName;
1046             } else {
1047                 return "";
1048             }
1049         }
1050
1051         /**
1052          * @return String representation
1053          */
1054         public String toString() {
1055             if (anchor) {
1056                 if (type == FUN) {
1057                     return "function: " + info.functionName + " & anchor: " + anchorName;
1058                 } else {
1059                     return anchorName;
1060                 }
1061             }
1062             return info.functionName;
1063         }
1064     }
1065
1066     /**
1067      * Inner class to get infos on anchor
1068      */
1069     public class Anchor {
1070
1071         private int line;
1072         private String name;
1073
1074         /**
1075          * Default constructor
1076          * @param line the line where the anchor is
1077          * @param name the anchor's name
1078          */
1079         public Anchor(int line, String name) {
1080             this.line = line;
1081             this.name = name;
1082         }
1083
1084         /**
1085          * {@inheritDoc}
1086          */
1087         public String toString() {
1088             return name;
1089         }
1090
1091         /**
1092          * @return the line number
1093          */
1094         public int getLine() {
1095             return line;
1096         }
1097     }
1098 }