/******************************************************************************* * Copyright (c) 2016 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences 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.core.dom.parser; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.eclipse.cdt.core.dom.ast.IASTExpression.ValueCategory; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IValue; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType; import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.EvalFixed; import org.eclipse.core.runtime.CoreException; public final class CStringValue implements IValue { private static final Map<Character, Character> escapeSequences; static { Map<Character, Character> map = new HashMap<Character, Character>(); map.put('\'', '\''); map.put('"', '"'); map.put('?', '?'); map.put('\\', '\\'); map.put('a', '\007'); map.put('b', '\b'); map.put('f', '\f'); map.put('n', '\n'); map.put('r', '\r'); map.put('t', '\t'); map.put('v', '\013'); escapeSequences = Collections.unmodifiableMap(map); } private final char[] fFixedValue; private String fParsedValue; private CStringValue(char[] fixedValue) { fFixedValue = fixedValue; } public static IValue create(char[] fixedValue) { return new CStringValue(fixedValue); } public String cStringValue() { if (fParsedValue == null) { fParsedValue = parseString(); } return fParsedValue; } private int indexOfStartQuote() { final int len = fFixedValue.length; int i = 0; while (i < len && fFixedValue[i] != '"') { ++i; } if (i >= len) { return -1; } else { return i; } } private int indexOfEndQuote() { int i = fFixedValue.length - 1; while (i >= 0 && fFixedValue[i] != '"') { --i; } if (i < 0) { return -1; } else { return i; } } private boolean isRawStringLiteral() { for (int i = 0; i < indexOfStartQuote(); ++i) { if (fFixedValue[i] == 'R') { return true; } } return false; } private int getDelimiterLength() { if (isRawStringLiteral()) { int i = indexOfStartQuote(); int len = 0; while (i < fFixedValue.length && fFixedValue[i] != '(') { ++i; ++len; } return len; } return 0; } private int getStart() { return indexOfStartQuote() + getDelimiterLength() + 1; } private int getEnd() { return indexOfEndQuote() - getDelimiterLength() - 1; } private String parseString() { // TODO: Reuse code between this and CPPASTLiteralExpression.computeStringLiteralSize(). boolean isRaw = isRawStringLiteral(); int end = getEnd(); StringBuilder builder = new StringBuilder(); for (int i = getStart(); i <= end; ++i) { if (!isRaw && fFixedValue[i] == '\\' && i < end) { ++i; //C-Strings are null-terminated. Therefore, a '\0' character //denotes the end of the string, even if the literal contains //more characters after that if (fFixedValue[i] == '0') { break; } else { i = parseEscapeSequence(i, builder); } } else { builder.append(fFixedValue[i]); } } return builder.toString(); } private int parseEscapeSequence(int i, StringBuilder builder) { char c = fFixedValue[i]; Character escapeSequence = escapeSequences.get(c); if (escapeSequence != null) { builder.append(escapeSequence); } else if (c == 'u' && i + 4 <= getEnd()) { StringBuilder hexStr = new StringBuilder(); ++i; for (int end = i + 4; i < end; ++i) { hexStr.append(fFixedValue[i]); } int codePoint = Integer.parseInt(hexStr.toString(), 16); builder.append(Character.toChars(codePoint)); } return i; } @Override public Long numericalValue() { return null; } @Override public Number numberValue() { return null; } @Override public int numberOfSubValues() { String str = cStringValue(); return str.length(); } @Override public ICPPEvaluation getSubValue(int index) { String str = cStringValue(); Character c = null; if (index >= 0 && index < str.length()) { c = str.charAt(index); } else if (index == str.length()) { c = '\0'; } if (c != null) { IValue val = IntegralValue.create(c); return new EvalFixed(CPPBasicType.CHAR, ValueCategory.PRVALUE, val); } return EvalFixed.INCOMPLETE; } @Override public ICPPEvaluation[] getAllSubValues() { return null; } @Override public ICPPEvaluation getEvaluation() { return null; } @Override public char[] getSignature() { return fFixedValue; } @Override public int hashCode() { return CharArrayUtils.hash(getSignature()); } @Override public boolean equals(Object obj) { if (!(obj instanceof CStringValue)) { return false; } final CStringValue rhs = (CStringValue) obj; if (fFixedValue != null) return CharArrayUtils.equals(fFixedValue, rhs.fFixedValue); return CharArrayUtils.equals(getSignature(), rhs.getSignature()); } @Deprecated @Override public char[] getInternalExpression() { return CharArrayUtils.EMPTY_CHAR_ARRAY; } @Deprecated @Override public IBinding[] getUnknownBindings() { return IBinding.EMPTY_BINDING_ARRAY; } @Override public void setSubValue(int position, ICPPEvaluation newValue) { } @Override public IValue clone() { char[] newFixedValue = Arrays.copyOf(fFixedValue, fFixedValue.length); return new CStringValue(newFixedValue); } @Override public void marshal(ITypeMarshalBuffer buf) throws CoreException { buf.putShort(ITypeMarshalBuffer.C_STRING_VALUE); buf.putCharArray(fFixedValue); } public static IValue unmarshal(short firstBytes, ITypeMarshalBuffer buf) throws CoreException { return new CStringValue(buf.getCharArray()); } /** * For debugging only. */ @Override public String toString() { return new String(fFixedValue); } }