c.s.tools.ide.analysis.modernize/src/com/sun/tools/ide/analysis/modernize/impl/YamlParser.java
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/c.s.tools.ide.analysis.modernize/src/com/sun/tools/ide/analysis/modernize/impl/YamlParser.java Wed Jun 07 20:23:29 2017 +0300
1.3 @@ -0,0 +1,357 @@
1.4 +/*
1.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
1.6 + *
1.7 + * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
1.8 + *
1.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
1.10 + * Other names may be trademarks of their respective owners.
1.11 + *
1.12 + * The contents of this file are subject to the terms of either the GNU
1.13 + * General Public License Version 2 only ("GPL") or the Common
1.14 + * Development and Distribution License("CDDL") (collectively, the
1.15 + * "License"). You may not use this file except in compliance with the
1.16 + * License. You can obtain a copy of the License at
1.17 + * http://www.netbeans.org/cddl-gplv2.html
1.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
1.19 + * specific language governing permissions and limitations under the
1.20 + * License. When distributing the software, include this License Header
1.21 + * Notice in each file and include the License file at
1.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
1.23 + * particular file as subject to the "Classpath" exception as provided
1.24 + * by Oracle in the GPL Version 2 section of the License file that
1.25 + * accompanied this code. If applicable, add the following below the
1.26 + * License Header, with the fields enclosed by brackets [] replaced by
1.27 + * your own identifying information:
1.28 + * "Portions Copyrighted [year] [name of copyright owner]"
1.29 + *
1.30 + * If you wish your version of this file to be governed by only the CDDL
1.31 + * or only the GPL Version 2, indicate your decision by adding
1.32 + * "[Contributor] elects to include this software in this distribution
1.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
1.34 + * single choice of license, a recipient has the option to distribute
1.35 + * your version of this file under either the CDDL, the GPL Version 2 or
1.36 + * to extend the choice of license to its licensees as provided above.
1.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
1.38 + * Version 2 license, then the option applies only if the new code is
1.39 + * made subject to such option by the copyright holder.
1.40 + *
1.41 + * Contributor(s): Ilia Gromov
1.42 + */
1.43 +package com.sun.tools.ide.analysis.modernize.impl;
1.44 +
1.45 +import static com.sun.tools.ide.analysis.modernize.impl.YamlParser.Diagnostics.UNDEFINED;
1.46 +import java.util.ArrayList;
1.47 +import java.util.Collections;
1.48 +import java.util.List;
1.49 +import java.util.StringTokenizer;
1.50 +import java.util.logging.Level;
1.51 +
1.52 +/**
1.53 + *
1.54 + * @author Ilia Gromov
1.55 + */
1.56 +public class YamlParser {
1.57 + // TODO check for no-replacements case
1.58 +
1.59 + private static final YamlParser INSTANCE = new YamlParser();
1.60 +
1.61 + public static YamlParser getDefault() {
1.62 + return INSTANCE;
1.63 + }
1.64 +
1.65 + private static final String OPEN_TAG = "---"; //NOI18N
1.66 + private static final String CLOSE_TAG = "..."; //NOI18N
1.67 +
1.68 + private static final String MAIN_SOURCE_FILE = "MainSourceFile"; //NOI18N
1.69 + private static final String CHECK_NAME = "CheckName"; //NOI18N
1.70 + private static final String LEVEL = "Level"; //NOI18N
1.71 + private static final String MESSAGE_FILE_PATH = "MessageFilePath"; //NOI18N
1.72 + private static final String MESSAGE_FILE_OFFSET = "MessageFileOffset"; //NOI18N
1.73 + private static final String MESSAGE = "Message"; //NOI18N
1.74 + private static final String REPLACEMENTS = "Replacements"; //NOI18N
1.75 + private static final String DIAGNOSTICS = "Diagnostics"; //NOI18N
1.76 +
1.77 + private static final String FILE_PATH = "FilePath"; //NOI18N
1.78 + private static final String OFFSET = "Offset"; //NOI18N
1.79 + private static final String LENGTH = "Length"; //NOI18N
1.80 + private static final String REPLACEMENT_TEXT = "ReplacementText"; //NOI18N
1.81 +
1.82 + // clang-modernize:
1.83 +// ---
1.84 +// MainSourceFile: /home/ilia/NetBeansProjects/CppApplication_46/main.cpp
1.85 +// Replacements:
1.86 +// - FilePath: /home/ilia/NetBeansProjects/CppApplication_46/newfile.h
1.87 +// Offset: 101
1.88 +// Length: 1
1.89 +// ReplacementText: nullptr
1.90 +// - FilePath: /home/ilia/NetBeansProjects/CppApplication_46/newfile1.h
1.91 +// Offset: 380
1.92 +// Length: 1
1.93 +// ReplacementText: nullptr
1.94 +// - FilePath: /home/ilia/NetBeansProjects/CppApplication_46/main.cpp
1.95 +// Offset: 152
1.96 +// Length: 1
1.97 +// ReplacementText: nullptr
1.98 +// ...
1.99 + // clang-tidy:
1.100 +// ---
1.101 +// MainSourceFile: ''
1.102 +// Diagnostics:
1.103 +// CheckName: misc-macro-parentheses
1.104 +// Replacements:
1.105 +// - FilePath: /media/SSD_/code/zdoom/main_1.cpp
1.106 +// Offset: 1352
1.107 +// Length: 0
1.108 +// ReplacementText: '('
1.109 +// - FilePath: /media/SSD_/code/zdoom/main_1.cpp
1.110 +// Offset: 1353
1.111 +// Length: 0
1.112 +// ReplacementText: ')'
1.113 +// Diagnostics:
1.114 +// CheckName: misc-macro-parentheses
1.115 +// Replacements:
1.116 +// - FilePath: /media/SSD_/code/zdoom/main_1.cpp
1.117 +// Offset: 1446
1.118 +// Length: 0
1.119 +// ReplacementText: '('
1.120 +// - FilePath: /media/SSD_/code/zdoom/main_1.cpp
1.121 +// Offset: 1447
1.122 +// Length: 0
1.123 +// ReplacementText: ')'
1.124 +// ...
1.125 + public List<Diagnostics> parseYaml(String output) {
1.126 + try {
1.127 + if (output.isEmpty()) {
1.128 + // No warnings in header = no 'Main Source File' element
1.129 + return Collections.EMPTY_LIST;
1.130 + }
1.131 + StringTokenizer st = new StringTokenizer(output, "\n"); //NOI18N
1.132 + String line;
1.133 +
1.134 + // ---
1.135 + line = st.nextToken();
1.136 +
1.137 + line = st.nextToken();
1.138 + String mainSourceFile = getValue(MAIN_SOURCE_FILE, line);
1.139 +
1.140 + // Replacements:
1.141 + line = st.nextToken();
1.142 + ClangTidyVersion version = line.contains(REPLACEMENTS)
1.143 + ? ClangTidyVersion.OLD_FORMAT
1.144 + : ClangTidyVersion.NEW_FORMAT;
1.145 +
1.146 + List<Diagnostics> list = version.parser.parse(st);
1.147 +
1.148 + return list;
1.149 + } catch (Exception ex) {
1.150 + ModernizeErrorProvider.LOG.log(Level.INFO, "Can't parse output line");
1.151 + return Collections.EMPTY_LIST;
1.152 + }
1.153 + }
1.154 +
1.155 + private static List<Replacement> parseReplacements(StringTokenizer st) {
1.156 + List<Replacement> replacements = new ArrayList<Replacement>();
1.157 + String line;
1.158 +
1.159 + // eat Replacements:
1.160 + line = st.nextToken();
1.161 +
1.162 + if (!line.contains(REPLACEMENTS)) {
1.163 + return replacements;
1.164 + }
1.165 +
1.166 + while (st.hasMoreTokens()) {
1.167 + line = st.nextToken();
1.168 +
1.169 + if (line.equals(CLOSE_TAG) || !line.contains(FILE_PATH)) {
1.170 + break;
1.171 + }
1.172 +
1.173 + String filePath = getValue(FILE_PATH, line);
1.174 +
1.175 + line = st.nextToken();
1.176 + int offset = Integer.parseInt(getValue(OFFSET, line));
1.177 +
1.178 + line = st.nextToken();
1.179 + int length = Integer.parseInt(getValue(LENGTH, line));
1.180 +
1.181 + line = st.nextToken();
1.182 + String replacementText = getValue(REPLACEMENT_TEXT, line);
1.183 + while (replacementText == null && st.hasMoreElements()) {
1.184 + line = line + "\n" + st.nextElement(); //NOI18N
1.185 + replacementText = getValue(REPLACEMENT_TEXT, line);
1.186 + }
1.187 +
1.188 + Replacement replacement = new Replacement(filePath, offset, length, replacementText);
1.189 + replacements.add(replacement);
1.190 +
1.191 + }
1.192 + return replacements;
1.193 + }
1.194 +
1.195 + private static String getValue(String key, String line) {
1.196 + int keyStart = line.indexOf(key);
1.197 + assert keyStart != -1;
1.198 + int keyEnd = keyStart + key.length() + 1;
1.199 + String trimmed = line.substring(keyEnd).trim();
1.200 + boolean isTextValue = trimmed.charAt(0) == '\'';
1.201 + if (isTextValue) {
1.202 + if (trimmed.length() == 1) {
1.203 + // '
1.204 + // abcd'
1.205 + return null;
1.206 + } else if (trimmed.charAt(trimmed.length() - 1) == '\'') {
1.207 + trimmed = trimmed.substring(1, trimmed.length() - 1);
1.208 + } else {
1.209 + return null;
1.210 + }
1.211 + }
1.212 + return trimmed;
1.213 + }
1.214 +
1.215 + public static class TranslationUnitDiagnostics {
1.216 +
1.217 + private final String mainSourceFilePath;
1.218 + private final String context;
1.219 + private final List<Diagnostics> diags;
1.220 +
1.221 + public TranslationUnitDiagnostics(String mainSourceFilePath, String context, List<Diagnostics> diags) {
1.222 + this.mainSourceFilePath = mainSourceFilePath;
1.223 + this.context = context;
1.224 + this.diags = diags;
1.225 + }
1.226 +
1.227 + public String getMainSourceFilePath() {
1.228 + return mainSourceFilePath;
1.229 + }
1.230 +
1.231 + public String getContext() {
1.232 + return context;
1.233 + }
1.234 +
1.235 + public List<Diagnostics> getDiags() {
1.236 + return diags;
1.237 + }
1.238 + }
1.239 +
1.240 + public static class Diagnostics {
1.241 +
1.242 + public static final String UNDEFINED = "undefined"; // NOI18N
1.243 +
1.244 + public enum Level {
1.245 + warning, error
1.246 + }
1.247 +
1.248 + private final String checkName;
1.249 + private final Diagnostics.Level level;
1.250 + private final String messageFilePath;
1.251 + private final int messageFileOffset;
1.252 + private final String message;
1.253 + private final List<Replacement> replacements;
1.254 +
1.255 + public Diagnostics(String checkName, Diagnostics.Level level, String messageFilePath, int messageFileOffset, String message, List<Replacement> replacements) {
1.256 + this.checkName = checkName;
1.257 + this.level = level;
1.258 + this.messageFilePath = messageFilePath;
1.259 + this.messageFileOffset = messageFileOffset;
1.260 + this.message = message;
1.261 + this.replacements = replacements;
1.262 + }
1.263 +
1.264 + public String getCheckName() {
1.265 + return checkName;
1.266 + }
1.267 +
1.268 + public Level getLevel() {
1.269 + return level;
1.270 + }
1.271 +
1.272 + public String getMessageFilePath() {
1.273 + return messageFilePath;
1.274 + }
1.275 +
1.276 + public int getMessageFileOffset() {
1.277 + return messageFileOffset;
1.278 + }
1.279 +
1.280 + public String getMessage() {
1.281 + return message;
1.282 + }
1.283 +
1.284 + public List<Replacement> getReplacements() {
1.285 + return replacements;
1.286 + }
1.287 + }
1.288 +
1.289 + public static class Replacement {
1.290 +
1.291 + public final String filePath;
1.292 + public final int offset;
1.293 + public final int length;
1.294 + public final String replacementText;
1.295 +
1.296 + public Replacement(String FilePath, int offset, int length, String replacementText) {
1.297 + this.filePath = FilePath;
1.298 + this.offset = offset;
1.299 + this.length = length;
1.300 + this.replacementText = replacementText;
1.301 + }
1.302 + }
1.303 +
1.304 + private static enum ClangTidyVersion {
1.305 + NEW_FORMAT(new NewFormatParser()),
1.306 + OLD_FORMAT(new OldFormatParser());
1.307 +
1.308 + public final TidyParser parser;
1.309 +
1.310 + private ClangTidyVersion(TidyParser parser) {
1.311 + this.parser = parser;
1.312 + }
1.313 + }
1.314 +
1.315 + private static abstract class TidyParser {
1.316 +
1.317 + abstract List<Diagnostics> parse(StringTokenizer st);
1.318 + }
1.319 +
1.320 + private static class NewFormatParser extends TidyParser {
1.321 +
1.322 + @Override
1.323 + public List<Diagnostics> parse(StringTokenizer st) {
1.324 + List<Diagnostics> list = new ArrayList<Diagnostics>();
1.325 +
1.326 + String line;
1.327 + while (st.hasMoreElements()) {
1.328 + line = st.nextToken();
1.329 + String checkName = getValue(CHECK_NAME, line);
1.330 +
1.331 + line = st.nextToken();
1.332 + Diagnostics.Level level = Diagnostics.Level.valueOf(getValue(LEVEL, line).toLowerCase());
1.333 +
1.334 + line = st.nextToken();
1.335 + String messageFilePath = getValue(MESSAGE_FILE_PATH, line);
1.336 +
1.337 + line = st.nextToken();
1.338 + int messageFileOffset = Integer.parseInt(getValue(MESSAGE_FILE_OFFSET, line));
1.339 +
1.340 + line = st.nextToken();
1.341 + String message = getValue(MESSAGE, line);
1.342 +
1.343 + List<Replacement> replacements = parseReplacements(st);
1.344 +
1.345 + list.add(new Diagnostics(checkName, level, messageFilePath, messageFileOffset, message, replacements));
1.346 + }
1.347 + return list;
1.348 + }
1.349 +
1.350 + }
1.351 +
1.352 + private static class OldFormatParser extends TidyParser {
1.353 +
1.354 + @Override
1.355 + public List<Diagnostics> parse(StringTokenizer st) {
1.356 + List<Replacement> parseReplacements = parseReplacements(st);
1.357 + return Collections.singletonList(new Diagnostics(UNDEFINED, Diagnostics.Level.warning, "", -1, "", parseReplacements)); //NOI18N
1.358 + }
1.359 + }
1.360 +}