001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2025 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 exc) {
236 final String exceptionMsg = String.format(Locale.ROOT,
237 "%s occurred while opening file %s.",
238 exc.getClass().getSimpleName(), file.getPath());
239 throw new CheckstyleException(exceptionMsg, exc);
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}