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