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