001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2017 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 java.io.File;
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.List;
027import java.util.Locale;
028
029import antlr.ANTLRException;
030import com.puppycrawl.tools.checkstyle.TreeWalker;
031import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
032import com.puppycrawl.tools.checkstyle.api.DetailAST;
033import com.puppycrawl.tools.checkstyle.api.FileContents;
034import com.puppycrawl.tools.checkstyle.api.FileText;
035
036/**
037 * Model for checkstyle frame.
038 * @author Vladislav Lisetskiy
039 */
040public class MainFrameModel {
041
042    /**
043     * Parsing modes which available in GUI.
044     */
045    public enum ParseMode {
046
047        /** Only Java tokens without comments. */
048        PLAIN_JAVA("Plain Java"),
049
050        /** Java tokens and comment nodes (singleline comments and block comments). */
051        JAVA_WITH_COMMENTS("Java with comments"),
052
053        /**
054         * Java tokens, comments and Javadoc comments nodes
055         * (which are parsed from block comments).
056         */
057        JAVA_WITH_JAVADOC_AND_COMMENTS("Java with comments and Javadocs");
058
059        /**
060         * Mode's short description.
061         */
062        private final String description;
063
064        /**
065         * Provides description.
066         * @param descr description
067         */
068        ParseMode(String descr) {
069            description = descr;
070        }
071
072        @Override
073        public String toString() {
074            return description;
075        }
076    }
077
078    /** Lines to position map. */
079    private final List<Integer> linesToPosition = new ArrayList<>();
080
081    /** Parse tree model. */
082    private final ParseTreeTableModel parseTreeTableModel;
083
084    /** Current mode. */
085    private ParseMode parseMode = ParseMode.PLAIN_JAVA;
086
087    /** The file which is being parsed. */
088    private File currentFile;
089
090    /** Text for a frame's text area. */
091    private String text;
092
093    /** Title for the main frame. */
094    private String title = "Checkstyle GUI";
095
096    /** Whether the reload action is enabled. */
097    private boolean reloadActionEnabled;
098
099    /** Instantiate the model. */
100    public MainFrameModel() {
101        parseTreeTableModel = new ParseTreeTableModel(null);
102    }
103
104    /**
105     * Set current parse mode.
106     * @param mode ParseMode enum.
107     */
108    public void setParseMode(ParseMode mode) {
109        parseMode = mode;
110    }
111
112    /**
113     * Get parse tree table model.
114     * @return parse tree table model.
115     */
116    public ParseTreeTableModel getParseTreeTableModel() {
117        return parseTreeTableModel;
118    }
119
120    /**
121     * Get text to display in a text area.
122     * @return text to display in a text area.
123     */
124    public String getText() {
125        return text;
126    }
127
128    /**
129     * @return title for the main frame.
130     */
131    public String getTitle() {
132        return title;
133    }
134
135    /**
136     * @return true if the reload action is enabled.
137     */
138    public boolean isReloadActionEnabled() {
139        return reloadActionEnabled;
140    }
141
142    /**
143     * Whether a file chooser should accept the file as a source file.
144     * @param file the file to check.
145     * @return true if the file should be accepted.
146     */
147    public static boolean shouldAcceptFile(File file) {
148        return file.isDirectory() || file.getName().endsWith(".java");
149    }
150
151    /**
152     * Get the directory of the last loaded file.
153     * @return directory of the last loaded file.
154     */
155    public File getLastDirectory() {
156        File lastDirectory = null;
157        if (currentFile != null) {
158            lastDirectory = new File(currentFile.getParent());
159        }
160        return lastDirectory;
161    }
162
163    /**
164     * Get current file.
165     * @return current file.
166     */
167    public File getCurrentFile() {
168        return currentFile;
169    }
170
171    /**
172     * Get lines to position map.
173     * @return lines to position map.
174     */
175    public List<Integer> getLinesToPosition() {
176        final List<Integer> copy = new ArrayList<>(linesToPosition);
177        return Collections.unmodifiableList(copy);
178    }
179
180    /**
181     * Open file and load the file.
182     * @param file the file to open.
183     * @throws CheckstyleException if the file can not be parsed.
184     */
185    public void openFile(File file) throws CheckstyleException {
186        if (file != null) {
187            try {
188                currentFile = file;
189                title = "Checkstyle GUI : " + file.getName();
190                reloadActionEnabled = true;
191                final DetailAST parseTree;
192
193                switch (parseMode) {
194                    case PLAIN_JAVA:
195                        parseTree = parseFile(file);
196                        break;
197                    case JAVA_WITH_COMMENTS:
198                    case JAVA_WITH_JAVADOC_AND_COMMENTS:
199                        parseTree = parseFileWithComments(file);
200                        break;
201                    default:
202                        throw new IllegalArgumentException("Unknown mode: " + parseMode);
203                }
204
205                parseTreeTableModel.setParseTree(parseTree);
206                parseTreeTableModel.setParseMode(parseMode);
207                final String[] sourceLines = getFileText(file).toLinesArray();
208
209                // clear for each new file
210                linesToPosition.clear();
211                // starts line counting at 1
212                linesToPosition.add(0);
213
214                final StringBuilder sb = new StringBuilder();
215                // insert the contents of the file to the text area
216                for (final String element : sourceLines) {
217                    linesToPosition.add(sb.length());
218                    sb.append(element).append(System.lineSeparator());
219                }
220                text = sb.toString();
221            }
222            catch (IOException | ANTLRException ex) {
223                final String exceptionMsg = String.format(Locale.ROOT,
224                    "%s occurred while opening file %s.",
225                    ex.getClass().getSimpleName(), file.getPath());
226                throw new CheckstyleException(exceptionMsg, ex);
227            }
228        }
229    }
230
231    /**
232     * Parse a file and return the parse tree.
233     * @param file the file to parse.
234     * @return the root node of the parse tree.
235     * @throws IOException if the file could not be read.
236     * @throws ANTLRException if the file is not a Java source.
237     */
238    public DetailAST parseFile(File file) throws IOException, ANTLRException {
239        final FileText fileText = getFileText(file);
240        final FileContents contents = new FileContents(fileText);
241        return TreeWalker.parse(contents);
242    }
243
244    /**
245     * Parse a file and return the parse tree with comment nodes.
246     * @param file the file to parse.
247     * @return the root node of the parse tree.
248     * @throws IOException if the file could not be read.
249     * @throws ANTLRException if the file is not a Java source.
250     */
251    public DetailAST parseFileWithComments(File file) throws IOException, ANTLRException {
252        final FileText fileText = getFileText(file);
253        final FileContents contents = new FileContents(fileText);
254        return TreeWalker.parseWithComments(contents);
255    }
256
257    /**
258     * Get FileText from a file.
259     * @param file the file to get the FileText from.
260     * @return the FileText.
261     * @throws IOException if the file could not be read.
262     */
263    public FileText getFileText(File file) throws IOException {
264        return new FileText(file.getAbsoluteFile(),
265                System.getProperty("file.encoding", "UTF-8"));
266    }
267}