/******************************************************************************* * Copyright (c) 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Jan S. Rellermeyer, IBM Research - initial API and implementation *******************************************************************************/ package org.eclipse.concierge; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.concierge.ConciergeCollections.ParseResult; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; import org.osgi.framework.Version; import org.osgi.framework.VersionRange; import org.osgi.framework.namespace.PackageNamespace; public final class Utils { private static String[] EMPTY_STRING_ARRAY = new String[0]; private static final Pattern LIST_TYPE_PATTERN = Pattern .compile("List\\s*<\\s*([^\\s]*)\\s*>"); @SuppressWarnings("deprecation") private static final String SPECIFICATION_VERSION = Constants.PACKAGE_SPECIFICATION_VERSION; public static String[] splitString(final String values, final char delimiter) { return splitString(values, delimiter, Integer.MAX_VALUE); } static String[] splitString(final String values, final char delimiter, final int limit) { if (values == null || values.length() == 0) { return EMPTY_STRING_ARRAY; } final List<String> tokens = new ArrayList<String>(values.length() / 10); final char[] chars = values.toCharArray(); final int len = chars.length; int openingQuote = -1; int pointer = 0; int curr = 0; int matches = 0; // skip trailing whitespaces while (Character.isWhitespace(chars[curr])) { curr++; } pointer = curr; do { if (chars[curr] == '\\') { curr += 2; continue; } else if (chars[curr] == '"') { if (openingQuote < 0) { openingQuote = curr; } else { openingQuote = -1; } curr++; continue; } else if (chars[curr] == delimiter && openingQuote < 0) { matches++; if (matches > limit) { break; } // scan back to skip whitepspaces int endPointer = curr - 1; while (endPointer > 0 && Character.isWhitespace(chars[endPointer])) { endPointer--; } // copy from pointer to current - 1 final int count = endPointer - pointer + 1; if (count > 0) { tokens.add(new String(chars, pointer, count)); } curr++; // scan forward to skip whitespaces while (curr < len && Character.isWhitespace(chars[curr])) { curr++; } pointer = curr; continue; } curr++; } while (curr < len); if (openingQuote > -1) { throw new IllegalArgumentException( "Unmatched quotation mark at position " + openingQuote); } // scan back to skip whitepspaces int endPointer = len - 1; while (endPointer > 0 && Character.isWhitespace(chars[endPointer])) { endPointer--; } final int count = endPointer - pointer + 1; if (count > 0) { tokens.add(new String(chars, pointer, count)); } return tokens.toArray(new String[tokens.size()]); } public static ParseResult parseLiterals(final String[] literals, final int start) throws BundleException { final HashMap<String, String> directives = new HashMap<String, String>(); final HashMap<String, Object> attributes = new HashMap<String, Object>(); for (int i = start; i < literals.length; i++) { final String[] parts = splitString(literals[i], '=', 1); final String name = parts[0].trim(); final int e = name.length() - 1; if (name.charAt(e) == ':') { // directive final String directive = name.substring(0, e).trim(); if (directives.containsKey(directive)) { throw new BundleException("Duplicate directive '" + directive + "'"); } directives.put(directive, unQuote(parts[1].trim())); } else { // attribute if (attributes.containsKey(name)) { throw new BundleException("Duplicate attribute " + name); } final String[] nameParts = splitString(name, ':'); if (nameParts.length > 1) { if (nameParts.length != 2) { throw new BundleException("Illegal attribute name " + name); } attributes .put(nameParts[0], createValue(nameParts[1].trim(), unQuote(parts[1]))); } else { if (Constants.VERSION_ATTRIBUTE.equals(name) && parts[1].indexOf(',') == -1) { attributes.put(name, new Version(unQuote(parts[1].trim()))); } else { attributes.put(name, unQuote(parts[1].trim())); } } } } return new ParseResult(directives, attributes); } private static final short STRING_TYPE = 0; private static final short VERSION_TYPE = 1; private static final short LONG_TYPE = 2; private static final short DOUBLE_TYPE = 3; private static Object createValue(final String type, final String valueStr) throws BundleException { final Matcher matcher = LIST_TYPE_PATTERN.matcher(type); if (matcher.matches() || "List".equals(type)) { final short elementType = matcher.matches() ? getType(matcher .group(1)) : STRING_TYPE; final List<Object> list = new ArrayList<Object>(); final String[] valueStrs = splitString(valueStr, ','); for (int i = 0; i < valueStrs.length; i++) { list.add(createValue0(elementType, valueStrs[i])); } return list; } else { return createValue0(getType(type), valueStr); } } private static short getType(final String type) { if ("String".equals(type)) { return STRING_TYPE; } if ("Version".equals(type)) { return VERSION_TYPE; } if ("Long".equals(type)) { return LONG_TYPE; } if ("Double".equals(type)) { return DOUBLE_TYPE; } return -1; } private static Object createValue0(final short type, final String valueStr) { switch (type) { case STRING_TYPE: return valueStr; case VERSION_TYPE: return new Version(valueStr.trim()); case LONG_TYPE: return new Long(valueStr.trim()); case DOUBLE_TYPE: return new Double(valueStr.trim()); } throw new IllegalStateException("invalid type " + type); } // TODO: fold into splitString? public static String unQuote(final String quoted) { final String quoted1 = quoted.trim(); final int len = quoted1.length(); final int start = quoted1.charAt(0) == '"' ? 1 : 0; final int end = quoted1.charAt(quoted1.length() - 1) == '"' ? len - 1 : len; return start == 0 && end == len ? quoted : quoted1 .substring(start, end); } public static String createFilter(final String namespace, final String req, final Map<String, Object> attributes) throws BundleException { final Object version = attributes.get(Constants.VERSION_ATTRIBUTE); if (PackageNamespace.PACKAGE_NAMESPACE.equals(namespace)) { if (version != null && attributes.containsKey(SPECIFICATION_VERSION)) { if (!new Version(Utils.unQuote((String) attributes .get(SPECIFICATION_VERSION))).equals(version)) { throw new BundleException( "both version and specification-version are given for the import " + req); } else { attributes.remove(SPECIFICATION_VERSION); } } } final StringBuffer buffer = new StringBuffer(); buffer.append('('); buffer.append(namespace); buffer.append('='); buffer.append(req); buffer.append(')'); if (attributes.size() == 0) { return buffer.toString(); } buffer.insert(0, "(&"); for (final Map.Entry<String, Object> attribute : attributes.entrySet()) { final String key = attribute.getKey(); final Object value = attribute.getValue(); if (Constants.VERSION_ATTRIBUTE.equals(key) || Constants.BUNDLE_VERSION_ATTRIBUTE.equals(key)) { if (value instanceof String) { final VersionRange range = new VersionRange( Utils.unQuote((String) value)); if (range.getRight() == null) { buffer.append('('); buffer.append(key); buffer.append(">="); buffer.append(range.getLeft()); buffer.append(')'); } else { boolean open = range.getLeftType() == VersionRange.LEFT_OPEN; buffer.append(open ? "(!(" : "("); buffer.append(key); buffer.append(open ? "<=" : ">="); buffer.append(range.getLeft()); buffer.append(open ? "))" : ")"); open = range.getRightType() == VersionRange.RIGHT_OPEN; buffer.append(open ? "(!(" : "("); buffer.append(key); buffer.append(open ? ">=" : "<="); buffer.append(range.getRight()); buffer.append(open ? "))" : ")"); } } else { buffer.append('('); buffer.append(key); buffer.append(">="); buffer.append(value); buffer.append(')'); } continue; } buffer.append("("); buffer.append(key); buffer.append("="); buffer.append(value); buffer.append(")"); } buffer.append(")"); return buffer.toString(); } }