001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package eu.cqse.check.framework.scanner;
018
019import java.io.File;
020import java.util.Collections;
021import java.util.EnumSet;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Set;
025
026import org.conqat.lib.commons.collections.CollectionUtils;
027import org.conqat.lib.commons.collections.ListMap;
028import org.conqat.lib.commons.filesystem.FileSystemUtils;
029import org.conqat.lib.commons.js_export.ExportToJavaScript;
030import org.conqat.lib.commons.resources.Resource;
031
032/**
033 * Enumeration class for the languages support by the scanner framework.
034 *
035 * <strong>This class is used for communication with IDE clients (via the
036 * {@link org.conqat.engine.service.shared.client.IdeServiceClient}), so special
037 * care has to be taken when changing its signature!</strong>
038 */
039@ExportToJavaScript
040public enum ELanguage {
041
042        // we need to use the ugly workaround with asHashSet here as we cannot use
043        // varargs twice in the constructor
044
045        /** Java */
046        JAVA("Java", true, "java"),
047
048        /** C/C++ */
049        CPP("C/C++", true, "cpp", "cc", "c", "h", "hh", "hpp", "cxx", "hxx", "inl", "inc", "pc"),
050
051        /** OpenCL C/C++ */
052        OPEN_CL("OpenCL C/C++", true, "cl"),
053
054        /** Rust */
055        RUST("Rust", true, "rs"),
056
057        /** Visual Basic */
058        VB("Visual Basic", false, "vb", "frm", "cls", "bas"),
059
060        /** PL/I */
061        PL1("PL/I", false, "pl1", "pli"),
062
063        /** COBOL */
064        COBOL("COBOL", false, "cbl", "cob", "cobol", "cpy", "eco"),
065
066        /** C# */
067        CS("C#", true, "cs"),
068
069        /** ABAP */
070        ABAP("ABAP", false, "abap"),
071
072        /**
073         * ABAP Dictionary. This is a textual representation of objects in the ABAP
074         * Dictionary (aka DDIC). The format is based on the textual format for
075         * structure and database table definition which is available starting with SAP
076         * ABP Platform v7.51 or v7.52 respectively. See
077         * <a>https://help.sap.com/doc/abapdocu_752_index_htm/7.52/de-DE/abenddic_tools.htm</a>.
078         * This format is again based on the SAP CDS DDL
079         * <a>https://help.sap.com/doc/abapdocu_752_index_htm/7.52/de-DE/abencds_f1_ddl_syntax.htm</a>.
080         * If available, we export ABAP DDIC objects in the textual representation as
081         * provided by the SAP system, otherwise our Teamscale Connector for the SAP
082         * ABAP Platform serializes the text in a similar format.
083         */
084        ABAP_DDIC("ABAP Dictionary", false, "abap_ddic"),
085
086        /** Ada */
087        ADA("Ada", false, "ada", "ads", "adb"),
088
089        /** Natural language text */
090        TEXT("Plain Text", false, "txt"),
091
092        /** XML */
093        XML("XML", true, "xml", "xsl", "xslt", "architecture", "cqb", "csproj", "config"),
094
095        /** HANA SQLScript */
096        SQLSCRIPT("HANA SQLScript", false, "sql", "hdbprocedure", "hdbfunction", "hdbscalarfunction", "hdbtablefunction"),
097
098        /**
099         * HANA Views XML. This includes view specifications which of SAP HANA database
100         * which are stored in XML. Does NOT include HANA code which is not XML (e.g.
101         * *.hdbview)
102         */
103        HANA_VIEW("HANA View", true, "analyticview", "attributeview", "calculationview"),
104
105        /** PL/SQL */
106        PLSQL("PL/SQL", false, "sql", "pks", "pkb", "trg", "fnc", "typ", "tyb", "prc", "plsql"),
107
108        /** Python */
109        PYTHON("Python", true, "py"),
110
111        /** T-SQL aka Transact SQL. */
112        TSQL("Transact-SQL", false, "tsql"),
113
114        /** Matlab */
115        MATLAB("Matlab", true, "m"),
116
117        /** PHP */
118        PHP("PHP", true, "php", "php3", "php4", "php5"),
119
120        /** Ruby */
121        RUBY("Ruby", true, "rb"),
122
123        /**
124         * JavaScript including EcmaScript and TypeScript.
125         * <p>
126         * Note that the statement oracle only works if semicolons are used
127         * consistently. However, semicolons are optional in JavaScript (rules described
128         * here: http://bclary.com/2004/11/07/#a-7.9), but to determine end of statement
129         * in this case requires a full blown parser (hard to decide locally in some
130         * cases). As most coding guidelines recommend using semicolons anyway, we stick
131         * with this solution.
132         */
133        JAVASCRIPT("JavaScript/TypeScript", true, "js", "sj", "ts", "tsx"),
134
135        /** The language used within the M/Text printing system. */
136        MTEXT("M/TEXT", true, "mtx"),
137
138        /**
139         * The "Just Your Average Computer Company Procedural Language". A C-like
140         * language being part of the Panther framework developed by the company
141         * Prolifics. The language is used in the archive system d.3 developed by the
142         * company "d.velop".
143         */
144        JPL("JPL", true, "jpl"),
145
146        /**
147         * Use this for languages for which no dedicated scanner is available. Creates a
148         * token per line (and creates EOL tokens).
149         */
150        LINE("Line-based Text", false),
151
152        /** Delphi */
153        DELPHI("Delphi", false, "pas", "dpr"),
154
155        /**
156         * IEC 61131-3 Structured Text. We understand both the code (PU) and variable
157         * structure (SV).
158         * <ul>
159         * <li>.pou files contain program code (written in ST language)</li>
160         * <li>.dt files contain type declarations (written in ST language)</li>
161         * </ul>
162         * Sadly, the file endings and format are not standardized in IEC-61131-3.
163         */
164        IEC61131("IEC 61131-3 ST", true, "pu", "sv", "st", "scl", "pou", "dt", "var"),
165
166        /** Fortran */
167        FORTRAN("Fortran", false, "f", "for", "f77", "f90", "f95", "f03"),
168
169        /** Xtend */
170        XTEND("Xtend", true, "xtend"),
171
172        /** Swift */
173        SWIFT("Swift", true, "swift"),
174
175        /** OCaml */
176        OCAML("OCaml", true, "ml", "mli"),
177
178        /** Opentext Oscript */
179        OSCRIPT("OScript", true, "osx", "lxe", "os"),
180
181        /** Groovy */
182        GROOVY("Groovy", true, "groovy"),
183
184        /** Requirement documents. */
185        NL_REQUIREMENTS("Natural Language Requirements", false),
186
187        /** Requirement documents. */
188        NL_TESTS("Natural Language Tests", false),
189
190        /** Simulink and Stateflow. */
191        SIMULINK("Simulink and Stateflow", false, "mdl", "slx", "sldd"),
192
193        /** Gosu (https://gosu-lang.github.io/). */
194        GOSU("Gosu", true, "gsp", "gs", "gsx", "gr", "grs"),
195
196        /** Kotlin. */
197        KOTLIN("Kotlin", true, "kt", "kts", "ktm"),
198
199        /** Objective C. */
200        OBJECTIVE_C("Objective-C", true, "m", "h"),
201
202        /** JavaDoc */
203        JAVADOC("JavaDoc", true),
204
205        /** Go */
206        GO("Go", true, "go");
207
208        /** List of languages that do not have methods. */
209        private static final EnumSet<ELanguage> LANGUAGES_WITHOUT_METHODS = EnumSet.of(ABAP_DDIC, LINE, NL_REQUIREMENTS,
210                        NL_TESTS, TEXT, XML, HANA_VIEW);
211
212        /** This maps from extensions to languages. */
213        private static final ListMap<String, ELanguage> EXTENSION_2_LANGUAGE_MAP = new ListMap<>();
214
215        /** Initialize {@link #EXTENSION_2_LANGUAGE_MAP}. */
216        static {
217                for (ELanguage language : values()) {
218                        for (String extension : language.extensions) {
219                                EXTENSION_2_LANGUAGE_MAP.add(extension.toLowerCase(), language);
220                        }
221                }
222        }
223
224        /** The readable name of the language, to be used, e.g., in a UI. */
225        private final String readableName;
226
227        /** Whether the language is case sensitive. */
228        private final boolean caseSensitive;
229
230        /** File extensions commonly used for this language. */
231        private final String[] extensions;
232
233        /** Create language. */
234        ELanguage(String readableName, boolean caseSensitive, String... extensions) {
235                this.readableName = readableName;
236                this.caseSensitive = caseSensitive;
237                this.extensions = extensions;
238        }
239
240        /** Returns {@link #readableName}. */
241        public String getReadableName() {
242                return readableName;
243        }
244
245        /** Return whether the language is case sensitive. */
246        public boolean isCaseSensitive() {
247                return caseSensitive;
248        }
249
250        /** Get the file extensions commonly used for this language. */
251        public String[] getFileExtensions() {
252                return CollectionUtils.copyArray(extensions);
253        }
254
255        /**
256         * Gets the {@link ELanguage} value corresponding to the extension of the
257         * resource. Returns null if no extension was found. If there are multiple
258         * possible languages, the first one is returned.
259         */
260        public static ELanguage fromResource(Resource resource) {
261                return fromFileExtension(resource.getExtension());
262        }
263
264        /**
265         * Gets the {@link ELanguage} value corresponding to the file extension of the
266         * path. Returns null if no extension was found. If there are multiple possible
267         * languages, the first one is returned. This method should only be used for
268         * test code or as a fallback since file extensions may match multiple
269         * {@link ELanguage}s.
270         */
271        public static ELanguage fromPath(String path) {
272                return fromFile(new File(path));
273        }
274
275        /**
276         * Gets the {@link ELanguage} value corresponding to the file extension of the
277         * file. Returns null if no extension was found. If there are multiple possible
278         * languages, the first one is returned. This method should only be used for
279         * test code or as a fallback since file extensions may match multiple
280         * {@link ELanguage}s.
281         */
282        public static ELanguage fromFile(File file) {
283                return fromFileExtension(FileSystemUtils.getFileExtension(file));
284        }
285
286        /**
287         * Gets the {@link ELanguage} value corresponding to the given file extension
288         * (without a dot). Returns {@link ELanguage#LINE} as a fallback if no language
289         * was registered for the extension. If there are multiple possible languages
290         * registered for the extension, however, the first one is returned.
291         */
292        public static ELanguage fromFileExtension(String extension) {
293                if (extension == null) {
294                        return ELanguage.LINE;
295                }
296
297                List<ELanguage> result = EXTENSION_2_LANGUAGE_MAP.getCollection(extension.toLowerCase());
298
299                if (result == null || result.isEmpty()) {
300                        return ELanguage.LINE;
301                }
302                return result.get(0);
303        }
304
305        /** Returns all languages for the given file extension */
306        public static Set<ELanguage> getAllLanguagesForExtension(String extension) {
307                if (extension == null) {
308                        return Collections.emptySet();
309                }
310                return new HashSet<>(EXTENSION_2_LANGUAGE_MAP.getCollectionOrEmpty(extension.toLowerCase()));
311        }
312
313        /**
314         * Returns all {@link ELanguage}s matching the file extension of the given file
315         * path.
316         */
317        public static Set<ELanguage> getAllLanguagesForPath(String path) {
318                return getAllLanguagesForExtension(FileSystemUtils.getFileExtension(path));
319        }
320
321        /** @return Whether the given language has the concept of methods. */
322        public static boolean languageHasMethods(ELanguage language) {
323                return !LANGUAGES_WITHOUT_METHODS.contains(language);
324        }
325
326}