/* * Copyright (c) 2013, 2015 QNX Software Systems and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.eclipse.cdt.internal.qt.core.index; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.cdt.internal.qt.core.Activator; /** * Provides a parser for QMake output. */ public final class QMakeParser { public static final String KEY_QMAKE_VERSION = "QMAKE_VERSION"; public static final String KEY_QT_VERSION = "QT_VERSION"; public static final String KEY_QT_INSTALL_IMPORTS = "QT_INSTALL_IMPORTS"; public static final String KEY_QT_INSTALL_QML = "QT_INSTALL_QML"; public static final String KEY_QT_INSTALL_DOCS = "QT_INSTALL_DOCS"; public static final String KEY_QMAKE_INTERNAL_INCLUDED_FILES = "QMAKE_INTERNAL_INCLUDED_FILES"; public static final String KEY_SOURCES = "SOURCES"; public static final String KEY_HEADERS = "HEADERS"; public static final String KEY_INCLUDEPATH = "INCLUDEPATH"; public static final String KEY_DEFINES = "DEFINES"; public static final String KEY_RESOURCES = "RESOURCES"; public static final String KEY_FORMS = "FORMS"; public static final String KEY_OTHER_FILES = "OTHER_FILES"; public static final String KEY_QML_IMPORT_PATH = "QML_IMPORT_PATH"; /** * Parses QMake output via a specified reg. exp. * * @param regex the reg. exp. * @param reader the QMake output * @return the modifiable map of parsed key-value pairs * @throws IOException when io error happens */ public static Map<String, String> parse(Pattern regex, BufferedReader reader) throws IOException { Map<String, String> result = new LinkedHashMap<String, String>(); String line; while((line = reader.readLine()) != null) { Matcher m = regex.matcher(line); if (!m.matches() || m.groupCount() != 2) { Activator.log("qmake: cannot decode query line '" + line + '\''); } else { String key = m.group(1); String value = m.group(2); String oldValue = result.put(key, value); if (oldValue != null) Activator.log("qmake: duplicate keys in query info '" + line + "' was '" + oldValue + '\''); } } return result; } /** * Returns an unmodifiable list with 0-1 values for a specific QMake variable. * * @param map the map * @param key the QMake variable * @return the unmodifiable list of values */ public static List<String> singleValue(Map<String, String> map, String key) { String value = map.get(key); return value == null ? Collections.<String>emptyList() : Collections.singletonList(value); } /** * Returns an unmodifiable list of values for a specific QMake variable that is decoded as a list of values. * * @param map the map * @param key the QMake variable * @return the unmodifiable list of values */ public static List<String> qmake3DecodeValueList(Map<String, String> map, String key) { String value = map.get(key); if (value == null) { return Collections.emptyList(); } List<String> result = new ArrayList<String>(); for (String item : qmake3SplitValueList(value)) { result.add(qmake3DecodeValue(item)); } return Collections.unmodifiableList(result); } /** * Decodes a specified QMake variable value. * * @param value the value * @return the decoded value */ public static String qmake3DecodeValue(String value) { int length = value.length(); if (length >= 2 && value.charAt(0) == '"' && value.charAt(length - 1) == '"') { value = value.substring(1, length - 1); length = value.length(); } StringBuilder sb = new StringBuilder(length); for (int i = 0; i < length; i ++) { char c = value.charAt(i); if (c == '\\') { ++ i; if (i < length) { char next = value.charAt(i); switch (next) { case 'r': sb.append('\r'); break; case 'n': sb.append ('\n'); break; case 't': sb.append ('\t'); break; case '\\': case '\'': case '"': sb.append (next); break; case 'x': i += 2; if (i < length) { char first = value.charAt(i - 1); char second = value.charAt(i); if (first >= '0' && first <= '9' && second >= '0' && second <= '9') { sb.append ((char) ((first - '0') * 16 + (second - '0'))); } } } } } else { sb.append (c); } } return sb.toString(); } /** * Splits a specified QMake variable value into a list of values. * * @param value the value * @return the modifiable list of values */ private static List<String> qmake3SplitValueList(String value) { List<String> result = new ArrayList<String>(); StringBuilder sb = new StringBuilder(); char quote = 0; boolean hadWord = false; final int length = value.length(); for (int i = 0; i < length; i ++) { char c = value.charAt(i); if (quote == c) { quote = 0; hadWord = true; sb.append(c); continue; } switch (c) { case '"': case '\'': quote = c; hadWord = true; break; case ' ': case '\t': if (quote == 0) { if (hadWord) { result.add(sb.toString()); sb.delete(0, sb.length()); hadWord = false; } continue; } break; case '\\': if (i + 1 < length) { char nextChar = value.charAt(i + 1); if (nextChar == '\'' || nextChar == '"' || nextChar == '\\') { sb.append(c); c = nextChar; ++ i; } } //$FALL-THROUGH$ default: hadWord = true; break; } sb.append (c); } if (hadWord) { result.add(sb.toString()); } return Collections.unmodifiableList(result); } }