/******************************************************************************* * Copyright (c) 2011 Google, Inc 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 * * Contributors: * Sergey Prigogin (Google) - initial API and implementation *******************************************************************************/ package org.eclipse.cdt.internal.core.parser.scanner; import java.util.Arrays; import java.util.Comparator; import org.eclipse.cdt.core.parser.ISignificantMacros; import org.eclipse.cdt.core.parser.util.CharArrayObjectMap; import org.eclipse.cdt.core.parser.util.CharArrayUtils; /** * A set of static methods to encode Map<String, String> as an array of characters and to decode * it back. * * The map is encoded as: * <code><number_of_entries>,<key1><value1>...<keyN><valueN></code>. * <p> * Each string is encoded as: <code><number_of_characters>,<characters></code>. * A <code>null</code> string is encoded as a single comma. */ public class SignificantMacros implements ISignificantMacros { public static final char[] UNDEFINED = {}; public static final char[] DEFINED = {}; private static final int ENCODED_UNDEFINED = Character.MAX_VALUE; private static final int ENCODED_DEFINED = Character.MAX_VALUE-1; private static final Comparator<Object> SORTER = new Comparator<Object>() { public int compare(Object o1, Object o2) { return CharArrayUtils.compare((char[])o1, (char[])o2); } }; private final char[] fEncoded; private int fHash; public SignificantMacros(char[] encoded) { assert encoded != null; fEncoded= encoded; } public SignificantMacros(CharArrayObjectMap<char[]> sigMacros) { fEncoded= encode(sigMacros); } private char[] encode(CharArrayObjectMap<char[]> sigMacros) { StringBuilder buffer= new StringBuilder(); Object[] keys= sigMacros.keyArray(); Arrays.sort(keys, SORTER); for (Object key : keys) { char[] name= (char[]) key; char[] value= sigMacros.get(name); buffer.append((char) name.length).append(name); if (value == DEFINED) { buffer.append((char) ENCODED_DEFINED); } else if (value == UNDEFINED) { buffer.append((char) ENCODED_UNDEFINED); } else { buffer.append((char) value.length).append(value); } } int len= buffer.length(); char[] result= new char[len]; buffer.getChars(0, len, result, 0); return result; } @Override public int hashCode() { int h = fHash; if (h == 0) { char val[] = fEncoded; int len = fEncoded.length; for (int i = 0; i < len; i++) { h = 31*h + val[i]; } fHash = h; } return h; } @Override public boolean equals(Object obj) { return obj instanceof SignificantMacros && hashCode() == obj.hashCode() && CharArrayUtils.equals(fEncoded, ((SignificantMacros) obj).fEncoded); } public boolean accept(IVisitor visitor) { final char[] encoded = fEncoded; final int len = encoded.length; int i= 0; while (i < len) { final int len1 = encoded[i++]; int v= i + len1; if (v >= len) break; char[] macro= extract(encoded, i, len1); final int len2 = encoded[v++]; switch(len2) { case ENCODED_UNDEFINED: i= v; if (!visitor.visitUndefined(macro)) return false; break; case ENCODED_DEFINED: i= v; if (!visitor.visitDefined(macro)) return false; break; default: i= v+len2; if (i > len) break; if (!visitor.visitValue(macro, extract(encoded, v, len2))) return false; break; } } return true; } public char[] extract(final char[] source, int from, final int length) { char[] value= new char[length]; System.arraycopy(source, from, value, 0, length); return value; } public char[] encode() { return fEncoded; } /** * For debugging purposes. */ @SuppressWarnings("nls") @Override public String toString() { final StringBuilder buf= new StringBuilder(); buf.append('{'); accept(new IVisitor() { public boolean visitValue(char[] macro, char[] value) { buf.append(macro).append('=').append(value).append(','); return true; } public boolean visitUndefined(char[] macro) { buf.append(macro).append('=').append("null,"); return true; } public boolean visitDefined(char[] macro) { buf.append(macro).append('=').append("*,"); return true; } }); int buflen = buf.length(); if (buflen > 1) buf.setLength(buflen-1); buf.append('}'); return buf.toString(); } }