001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2016 the original author or authors.
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.gui;
021
022import antlr.ASTFactory;
023import antlr.collections.AST;
024import com.puppycrawl.tools.checkstyle.api.DetailAST;
025import com.puppycrawl.tools.checkstyle.api.TokenTypes;
026import com.puppycrawl.tools.checkstyle.utils.TokenUtils;
027
028/**
029 * The model that backs the parse tree in the GUI.
030 *
031 * @author Lars Kühne
032 */
033public class ParseTreeTablePModel {
034
035    /** Column names. */
036    private static final String[] COLUMN_NAMES = {
037        "Tree", "Type", "Line", "Column", "Text",
038    };
039
040    /**
041     * The root node of the tree table model.
042     */
043    private final Object root;
044
045    /**
046     * @param parseTree DetailAST parse tree.
047     */
048    public ParseTreeTablePModel(DetailAST parseTree) {
049        root = createArtificialTreeRoot();
050        setParseTree(parseTree);
051    }
052
053    /**
054     * Creates artificial tree root.
055     * @return artificial tree root.
056     */
057    private static DetailAST createArtificialTreeRoot() {
058        final ASTFactory factory = new ASTFactory();
059        factory.setASTNodeClass(DetailAST.class.getName());
060        return (DetailAST) factory.create(TokenTypes.EOF, "ROOT");
061    }
062
063    /**
064     * Set parse tree.
065     * @param parseTree DetailAST parse tree.
066     */
067    protected final void setParseTree(DetailAST parseTree) {
068        ((AST) root).setFirstChild(parseTree);
069    }
070
071    /**
072     * @return the number of available columns.
073     */
074    public int getColumnCount() {
075        return COLUMN_NAMES.length;
076    }
077
078    /**
079     * @param column the column number
080     * @return the name for column number {@code column}.
081     */
082    public String getColumnName(int column) {
083        return COLUMN_NAMES[column];
084    }
085
086    /**
087     * @param column the column number
088     * @return the type for column number {@code column}.
089     */
090    public Class<?> getColumnClass(int column) {
091        final Class<?> columnClass;
092
093        switch (column) {
094            case 0:
095                columnClass = ParseTreeTableModel.class;
096                break;
097            case 1:
098                columnClass = String.class;
099                break;
100            case 2:
101                columnClass = Integer.class;
102                break;
103            case 3:
104                columnClass = Integer.class;
105                break;
106            case 4:
107                columnClass = String.class;
108                break;
109            default:
110                columnClass = Object.class;
111        }
112        return columnClass;
113    }
114
115    /**
116     * @param node the node
117     * @param column the column number
118     * @return the value to be displayed for node {@code node},
119     *     at column number {@code column}.
120     */
121    public Object getValueAt(Object node, int column) {
122        final DetailAST ast = (DetailAST) node;
123        final Object value;
124
125        switch (column) {
126            case 1:
127                value = TokenUtils.getTokenName(ast.getType());
128                break;
129            case 2:
130                value = ast.getLineNo();
131                break;
132            case 3:
133                value = ast.getColumnNo();
134                break;
135            case 4:
136                value = ast.getText();
137                break;
138            default:
139                value = null;
140        }
141        return value;
142    }
143
144    /**
145     * Returns the child of parent at index.
146     * @param parent the node to get a child from.
147     * @param index the index of a child.
148     * @return the child of parent at index.
149     */
150    public Object getChild(Object parent, int index) {
151        final DetailAST ast = (DetailAST) parent;
152        int currentIndex = 0;
153        AST child = ast.getFirstChild();
154        while (currentIndex < index) {
155            child = child.getNextSibling();
156            currentIndex++;
157        }
158        return child;
159    }
160
161    /**
162     * Returns the number of children of parent.
163     * @param parent the node to count children for.
164     * @return the number of children of the node parent.
165     */
166    public int getChildCount(Object parent) {
167        final DetailAST ast = (DetailAST) parent;
168        return ast.getChildCount();
169    }
170
171    /**
172     * @return the root.
173     */
174    public Object getRoot() {
175        return root;
176    }
177
178    /**
179     * Whether the node is a leaf.
180     * @param node the node to check.
181     * @return true if the node is a leaf.
182     */
183    public boolean isLeaf(Object node) {
184        return getChildCount(node) == 0;
185    }
186
187    /**
188     * Return the index of child in parent.  If either <code>parent</code>
189     * or <code>child</code> is <code>null</code>, returns -1.
190     * If either <code>parent</code> or <code>child</code> don't
191     * belong to this tree model, returns -1.
192     *
193     * @param parent a node in the tree, obtained from this data source.
194     * @param child the node we are interested in.
195     * @return the index of the child in the parent, or -1 if either
196     *     <code>child</code> or <code>parent</code> are <code>null</code>
197     *     or don't belong to this tree model.
198     */
199    public int getIndexOfChild(Object parent, Object child) {
200        int index = -1;
201        for (int i = 0; i < getChildCount(parent); i++) {
202            if (getChild(parent, i).equals(child)) {
203                index = i;
204                break;
205            }
206        }
207        return index;
208    }
209
210    /**
211     * Indicates whether the the value for node {@code node},
212     * at column number {@code column} is editable.
213     *
214     * @param column the column number
215     * @return true if editable
216     */
217    public boolean isCellEditable(int column) {
218        return getColumnClass(column).equals(ParseTreeTablePModel.class);
219    }
220}