    001    /*
    002     * SourceParser.java
    003     * :folding=explicit:collapseFolds=1:
    004     *
    005     * Copyright (C) 2005 by Martin Raspe
    006     * (hertzhaft@biblhertz.it)
    007     *
    008     * This program is free software; you can redistribute it and/or
    009     * modify it under the terms of the GNU General Public License
    010     * as published by the Free Software Foundation; either version 2
    011     * of the License, or any later version.
    012     *
    013     * This program is distributed in the hope that it will be useful,
    014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
    015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    016     * GNU General Public License for more details.
    017     *
    018     * You should have received a copy of the GNU General Public License
    019     * along with this program; if not, write to the Free Software
    020     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    021     */
    022    package sidekick.enhanced;
    023
    024    import javax.swing.tree.*;
    025    import javax.swing.text.Position;
    026    import javax.swing.ImageIcon;
    027    import java.util.*;
    028    import java.util.regex.*;
    029
    030    import java.net.URL;
    031
    032    import org.gjt.sp.jedit.*;
    033    import org.gjt.sp.util.*;
    034    import org.gjt.sp.util.Log;
    035
    036    import sidekick.SideKickParsedData;
    037    import sidekick.SideKickParser;
    038
    039    import errorlist.*;
    040
    041
    042    /**
    043     * SourceParser: parses source and builds a sidekick structure tree
    044     * Parsers are based on regular expressions and will therefore
    045     * not able to correctly parse irregular source
    046     *
    047     * @author     Martin Raspe
    048     * @created    Oct 15, 2005
    049     * @modified   $Id: SourceParser.java,v 1.5 2006/03/23 11:57:01 hertzhaft Exp $
    050     * @version    $Revision: 1.5 $
    051     */
    052    public class SourceParser extends SideKickParser {
    053            //{{{ Icons
    054            public ImageIcon PACKAGE_ICON;
    055            public ImageIcon USE_ICON;
    056            public ImageIcon SUB_ICON;
    057            public ImageIcon PACKAGE_INVALID_ICON;
    058            public ImageIcon USE_INVALID_ICON;
    059            public ImageIcon SUB_INVALID_ICON;
    060            public ImageIcon COMMENT_ICON;
    061            //}}}
    062
    063            //{{{ vars
    064            public String LINE_COMMENT;
    065            public String COMMENT;  // title for block comments
    066            public String MAIN;     // title for default package
    067            public String USE;      // title for import modules
    068
    069            public String USE_KEY   = "---use---";
    070            public String SUB_KEY   = "---sub---";
    071            public String PKG_KEY   = "---pkg---";
    072
    073            protected SideKickParsedData data;
    074            protected PackageMap packages;
    075            protected ArrayList commentList;
    076            protected SourceAsset _asset;
    077            protected SourceAsset _pkgAsset;
    078
    079            protected Position _start;
    080            protected Position _end;
    081        protected int _lastLineNumber = 0;
    082            //}}}
    083
    084    /**      * Constructs a new SourceParser object
    085             *
    086             * @param name See sidekick.SidekickParser.
    087             */
    088            public SourceParser(String name) {
    089                    super(name);
    090                    loadIcons(name, getClass());
    091            }
    092
    093            public SourceParser(String name, Class cls) {
    094                    super(name);
    095                    loadIcons(name, cls);
    096            }
    097    /**      * Parses the given text and returns a tree model.
    098             *
    099             * @param buffer The buffer to parse.
    100             * @param errorSource An error source to add errors to.
    101             *
    102             * @return A new instance of the <code>SourceParsedData</code> class.
    103             */
    104            public SideKickParsedData parse(Buffer buffer, DefaultErrorSource errorSource) {
    105                    data = new SideKickParsedData(buffer.getName());
    106                    packages = new PackageMap(new PackageComparator());
    107                    commentList = new ArrayList();
    108                    parseBuffer(buffer, errorSource);
    109                    completePackageAsset(_end, _lastLineNumber);
    110                    Log.log(Log.DEBUG, this, "parsing completed");
    111                    buildTrees();
    112                    Log.log(Log.DEBUG, this, "tree built");
    113                    return data;
    114            }
    115
    116            protected void parseBuffer(Buffer buffer, DefaultErrorSource errorSource) {
    117                    }
    118
    119            protected void loadIcons(String name, Class cls) {
    120                    PACKAGE_ICON            = loadIcon(name, cls, "package-icon");
    121                    USE_ICON                = loadIcon(name, cls, "use-icon");
    122                    SUB_ICON                = loadIcon(name, cls, "sub-icon");
    123                    PACKAGE_INVALID_ICON    = loadIcon(name, cls, "package_invalid-icon");
    124                    USE_INVALID_ICON        = loadIcon(name, cls, "use-invalid-icon");
    125                    SUB_INVALID_ICON        = loadIcon(name, cls, "sub-invalid-icon");
    126                    COMMENT_ICON            = loadIcon(name, cls, "comment-icon");
    127            }
    128
    129            protected ImageIcon loadIcon(String name, Class cls, String icon) {
    130                    String iconfile = jEdit.getProperty("sidekick.parser." + name + "." + icon);
    131                    URL res = cls.getResource("/icons/" + iconfile);
    132                    if (res == null) return null;
    133                    return new ImageIcon(res);
    134            }
    135
    136            // Sets the end position of the most recently created asset.
    137            protected void completeAsset(Position end) {
    138                    if (_asset != null) _asset.setEnd(end);
    139                    _asset = null;
    140                    }
    141
    142            // Sets the end position of the most recently created asset.
    143            protected void completeAsset(Position end, String desc) {
    144                    if (_asset != null)
    145                            _asset.setLongDescription(desc);
    146                    completeAsset(end);
    147                    }
    148
    149            // Sets the end position of the most recently created asset.
    150            protected void completeAsset(Position end, int lineNo) {
    151                    if (_asset != null)
    152                            _asset.setLongDescription(
    153                                    _asset.getLongDescription()+ "-" + lineNo
    154                                    );
    155                    _lastLineNumber = lineNo;
    156                    completeAsset(end);
    157                    }
    158
    159            // Sets the end position of the most recently created package asset.
    160            protected void completePackageAsset(Position end, int lineNo) {
    161                    completeAsset(end, lineNo);
    162                    _asset = _pkgAsset;
    163                    completeAsset(end, lineNo);
    164            }
    165
    166        // Adds a multi-line asset to the list of its package
    167            protected void addAsset(String typ, String p, String name, int lineNo, Position start) {
    168                    completeAsset(start, lineNo);
    169                    _asset = new SourceAsset(name, lineNo, start);
    170                    packages.addToList(typ, p, _asset);
    171                    }
    172
    173            // Adds a one-line asset (don't complete the current _asset yet)
    174            protected void addLineAsset(String typ, String p, String name, int lineNo, Position start, Position end) {
    175                    SourceAsset a = new SourceAsset(name, lineNo, start);
    176                    a.setEnd(end);
    177                    packages.addToList(typ, p, a);
    178                    }
    179
    180            //Adds an asset to the pod list
    181            protected void addCommentAsset(String name, int lineNo, Position start) {
    182                    completeAsset(start, lineNo);
    183                    _asset = new SourceAsset(name, lineNo, start);
    184                    commentList.add(_asset);
    185                    }
    186
    187            // Adds a new package to the "packages" TreeMap.
    188            protected void addPackageAsset(String name, int lineNo, Position start) {
    189                    completePackageAsset(start, lineNo);
    190                    SourceAsset a = new SourceAsset(name, lineNo, start);
    191                    _pkgAsset = a;
    192            packages.addPackage(name, a);
    193                    }
    194
    195            // Builds the sidekick tree from the "packages" TreeMap
    196            protected void buildTrees() {
    197                    Set list = packages.keySet();
    198                    Iterator i = list.iterator();
    199                    while(i.hasNext()) {
    200                            String name = (String) i.next();
    201                            HashMap h = (HashMap) packages.get(name);
    202                            SourceAsset a = (SourceAsset) h.get(PKG_KEY);
    203                            a.setIcon(PACKAGE_ICON);
    204                            DefaultMutableTreeNode t = new DefaultMutableTreeNode(a);
    205                            newTree(t, USE, (ArrayList) h.get(USE_KEY), USE_ICON);
    206                            addList(t, (ArrayList) h.get(SUB_KEY), SUB_ICON);
    207                            data.root.add(t);
    208                            }
    209                    newTree(data.root, COMMENT, commentList, COMMENT_ICON);
    210                    }
    211
    212            // Builds a tree from asset list "list" and adds it to tree node "n".
    213            protected void newTree(DefaultMutableTreeNode n, String name, ArrayList list, ImageIcon icon) {
    214                    if (list.size() == 0) return;
    215                    SourceAsset first = (SourceAsset) list.get(0);
    216                    SourceAsset branch = new SourceAsset(name, first.getLineNo(), first.start);
    217                    branch.setIcon(icon);
    218                    DefaultMutableTreeNode t = new DefaultMutableTreeNode(branch);
    219                    addList(t, list, null);
    220                    n.add(t);
    221                    }
    222
    223            //Adds an Asset list "list" to a tree node "n"
    224            protected void addList(DefaultMutableTreeNode t, ArrayList list, ImageIcon icon) {
    225                    Collections.sort(list, new AssetComparator());
    226                    Iterator i = list.iterator();
    227                    while(i.hasNext()) {
    228                            SourceAsset a = (SourceAsset) i.next();
    229                            a.setIcon(icon);
    230                            t.add(new DefaultMutableTreeNode(a));
    231                            }
    232                    }
    233
    234            // match a line and return the specified group if found
    235            protected String find(String line, Pattern p, int g) {
    236                    Matcher m = p.matcher(line);
    237                    return m.find() ? m.group(g) : null;
    238                    }
    239
    240            // match a line and return package and subname if found
    241            protected String[] find2(String line, Pattern p) {
    242                    String[] res = new String[2];
    243                    Matcher m = p.matcher(line);
    244                    if (m.find()) {
    245                            res[0] = m.group(1);
    246                            res[1] = m.group(2);
    247                            }
    248                    else res = null;
    249                    return res;
    250                    }
    251
    252            // compare strings, ignore case
    253            protected class AssetComparator implements Comparator {
    254                    public int compare(Object o1, Object o2) {
    255                            String s1 = ((SourceAsset) o1).toString();
    256                            String s2 = ((SourceAsset) o2).toString();
    257                            return s1.toLowerCase().compareTo(s2.toLowerCase());
    258                            }
    259                    }
    260
    261            // sort package names
    262            protected class PackageComparator implements Comparator{
    263                    public int compare(Object o1, Object o2) {
    264                            String s1 = (String)o1;
    265                            String s2 = (String)o2;
    266                            int comp = s1.toLowerCase().compareTo(s2.toLowerCase());
    267                            if (comp == 0) return comp;
    268                            // pull up module MAIN
    269                            if (s1.equals(MAIN)) return -1;
    270                            if (s2.equals(MAIN)) return 1;
    271                            return comp;
    272                            }
    273                    }
    274
    275            // reflects perl script structure
    276            protected class PackageMap extends TreeMap {
    277                    public PackageMap() {
    278                            super();
    279                            }
    280
    281                    public PackageMap(PackageComparator pc) {
    282                            super(pc);
    283                            }
    284
    285                    // add a HashMap for a new package name
    286                    public HashMap addPackage(String p, SourceAsset a) {
    287                            if (this.containsKey(p))
    288                    return (HashMap) this.get(p);
    289                            HashMap h = new HashMap();
    290                            h.put(PKG_KEY, a);
    291                            h.put(USE_KEY, new ArrayList());
    292                            h.put(SUB_KEY, new ArrayList());
    293                            this.put(p, h);
    294                            return h;
    295                            }
    296
    297                    // get the package asset by name
    298                    public SourceAsset getPackageAsset(String p) {
    299                            if (! this.containsKey(p))
    300                                    return null;
    301                            HashMap h = (HashMap) this.get(p);
    302                            return (SourceAsset) h.get(PKG_KEY);
    303                            }
    304
    305                    // set the end position for a package asset
    306                    public void completePackageAsset(String p, Position end) {
    307                            SourceAsset a = getPackageAsset(p);
    308                            if (a != null) a.setEnd(end);
    309                            }
    310
    311                    public void completePackageAsset(String p, Position end, String desc) {
    312                            SourceAsset a = getPackageAsset(p);
    313                            if (a == null)
    314                            return;
    315                            a.setLongDescription(desc);
    316                            a.setEnd(end);
    317                            }
    318
    319                    public void completePackageAsset(String p, Position end, int lineNo) {
    320                            SourceAsset a = getPackageAsset(p);
    321                            if (a == null)
    322                            return;
    323                            a.setLongDescription(a.getLongDescription() + "-" + lineNo);
    324                            a.setEnd(end);
    325                            }
    326
    327                    // add an Asset of type "typ" to the TreeMap for package "p"
    328                    public void addToList(String typ, String p, SourceAsset a) {
    329                            SourceAsset pkg = new SourceAsset(p, a.getLineNo(), a.start);
    330                            HashMap h = addPackage(p, pkg);
    331                            ArrayList list = (ArrayList) h.get(typ);
    332                            if (list != null)
    333                            list.add(a);
    334                            else Log.log(Log.DEBUG, SourceParser.class,
    335                            "Entry " + typ + " not in PackageMap"
    336                            );
    337                            }
    338                    }
    339    }