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