| /******************************************************************************* |
| * Copyright (c) 2003, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.osgi.internal.serviceregistry; |
| |
| import java.lang.reflect.Array; |
| import java.util.*; |
| import org.eclipse.osgi.framework.util.CaseInsensitiveDictionaryMap; |
| import org.eclipse.osgi.internal.messages.Msg; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.Constants; |
| |
| /** |
| * Service properties. |
| * |
| * Supports case-insensitive key lookup. |
| */ |
| class ServiceProperties extends CaseInsensitiveDictionaryMap<String, Object> { |
| /** |
| * Create a properties object from a Dictionary. |
| * |
| * @param props The properties for this service. |
| * @throws IllegalArgumentException If a case-variants of a key are |
| * in the props parameter. |
| */ |
| ServiceProperties(Dictionary<String, ?> props) { |
| this(props, 0); |
| } |
| |
| /** |
| * Create a properties object from a Dictionary. |
| * |
| * @param props The properties for this service. |
| * @param extra Extra capacity in the map. |
| * @throws IllegalArgumentException If a case-variants of a key are |
| * in the props parameter. |
| */ |
| ServiceProperties(Dictionary<String, ?> props, int extra) { |
| super(initialCapacity((props == null) ? extra : props.size() + extra)); |
| if (props == null) { |
| return; |
| } |
| synchronized (props) { |
| Enumeration<?> keysEnum = props.keys(); |
| while (keysEnum.hasMoreElements()) { |
| Object key = keysEnum.nextElement(); |
| if (key instanceof String) { |
| String header = (String) key; |
| Object value = cloneValue(props.get(header)); |
| if (value != null && put(header, value) != null) { |
| throw new IllegalArgumentException(NLS.bind(Msg.HEADER_DUPLICATE_KEY_EXCEPTION, key)); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Create a properties object from a Map. |
| * |
| * @param props The properties for this service. |
| * @throws IllegalArgumentException If a case-variants of a key are |
| * in the props parameter. |
| */ |
| ServiceProperties(Map<String, ?> props) { |
| super(initialCapacity((props == null) ? 0 : props.size())); |
| if (props == null) { |
| return; |
| } |
| synchronized (props) { |
| for (Entry<?, ?> e : props.entrySet()) { |
| Object key = e.getKey(); |
| if (key instanceof String) { |
| String header = (String) key; |
| Object value = cloneValue(props.get(header)); |
| if (value != null && put(header, value) != null) { |
| throw new IllegalArgumentException(NLS.bind(Msg.HEADER_DUPLICATE_KEY_EXCEPTION, key)); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Attempt to clone the value if necessary and possible. |
| * |
| * For some strange reason, you can test to see if an Object is |
| * Cloneable but you can't call the clone method since it is |
| * protected on Object! |
| * |
| * @param value object to be cloned. |
| * @return cloned object or original object if we didn't clone it. |
| */ |
| static Object cloneValue(Object value) { |
| if (value == null) |
| return null; |
| if (value instanceof String) /* shortcut String */ |
| return value; |
| if (value instanceof Number) /* shortcut Number */ |
| return value; |
| if (value instanceof Character) /* shortcut Character */ |
| return value; |
| if (value instanceof Boolean) /* shortcut Boolean */ |
| return value; |
| |
| Class<?> clazz = value.getClass(); |
| if (clazz.isArray()) { |
| // Do an array copy |
| Class<?> type = clazz.getComponentType(); |
| int len = Array.getLength(value); |
| Object clonedArray = Array.newInstance(type, len); |
| System.arraycopy(value, 0, clonedArray, 0, len); |
| return clonedArray; |
| } |
| |
| if (value instanceof Cloneable) { |
| // must use reflection because Object clone method is protected!! |
| try { |
| return clazz.getMethod("clone", (Class<?>[]) null).invoke(value, (Object[]) null); //$NON-NLS-1$ |
| } catch (Exception e) { |
| /* clone is not a public method on value's class */ |
| } |
| } |
| return value; |
| } |
| |
| @Override |
| public String toString() { |
| Set<String> keys = keySet(); |
| |
| StringBuilder sb = new StringBuilder(20 * keys.size()); |
| |
| sb.append('{'); |
| |
| int n = 0; |
| for (String key : keys) { |
| if (!key.equals(Constants.OBJECTCLASS)) { |
| if (n > 0) |
| sb.append(", "); //$NON-NLS-1$ |
| |
| sb.append(key); |
| sb.append('='); |
| Object value = get(key); |
| if (value.getClass().isArray()) { |
| sb.append('['); |
| int length = Array.getLength(value); |
| for (int j = 0; j < length; j++) { |
| if (j > 0) |
| sb.append(','); |
| sb.append(Array.get(value, j)); |
| } |
| sb.append(']'); |
| } else { |
| sb.append(value); |
| } |
| n++; |
| } |
| } |
| |
| sb.append('}'); |
| |
| return sb.toString(); |
| } |
| } |