/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.tmatesoft.svn.core.internal.util.SVNHashMap; /** * The <b>SVNProperties</b> class represents an object wrapper for * <code>String</code> to {@link SVNPropertyValue} mappings where * <code>String</code> keys represent property names and values - property * values wrapped in {@link SVNPropertyValue} objects. * * <p> * This class is backed by a <code>Map</code> object and brings specific methods * useful for working with version controlled properties. * * <p> * Objects of this type are modifiable. * * @author TMate Software Ltd. * @version 1.3 * @since 1.2 */ public class SVNProperties implements Cloneable, Serializable { private static final long serialVersionUID = 1L; private Map<String, SVNPropertyValue> myProperties; /** * Creates a new <code>SVNProperties</code> object wrapping a given map with * properties. * * <p/> * <code>map</code> is not stored by this object, instead its contents are * copied into a new <code>Map</code> object (which will be backed by a new * <code>SVNProperties</code> object) according to the following rules: * * <ul> * <li/>if the value is of type <code>String</code>, then it's wrapped into * {@link SVNPropertyValue} using the * {@link SVNPropertyValue#create(String)} method; * <li/>if the value is of type <code>byte[]</code>, then it's wrapped into * {@link SVNPropertyValue} using the * {@link SVNPropertyValue#create(String, byte[])} method; * <li/>if the value is of type {@link SVNPropertyValue}, then it's not * copied but is put into a new map as is; * </ul> * * @param map * initial map holding properties * @return <code>SVNProperties</code> object; if <code>map</code> is <span * class="javakeyword">null</span>, returns an empty * <code>SVNProperties</code> object created as * <code>new SVNProperties()</code> * @see #SVNProperties() */ public static SVNProperties wrap(Map map) { if (map == null) { return new SVNProperties(); } Map propertiesMap = new SVNHashMap(); for (Iterator names = map.keySet().iterator(); names.hasNext();) { Object n = names.next(); if (!(n instanceof String)) { continue; } Object value = map.get(n); SVNPropertyValue v = null; if (value instanceof String) { v = SVNPropertyValue.create((String) value); } else if (value instanceof byte[]) { v = SVNPropertyValue.create(n.toString(), (byte[]) value); } else if (value instanceof SVNPropertyValue) { v = (SVNPropertyValue) value; } if (v != null) { propertiesMap.put(n, v); } } return new SVNProperties(propertiesMap); } /** * Returns an unmodifiable view of the specified <code>properties</code>. * Any attempt to modify the returned <code>SVNProperties</code> object * result in an <code>UnsupportedOperationException</code>. * * @param properties * <code>SVNProperties</code> object for which an unmodifiable * view is to be returned. * @return an unmodifiable view of the specified properties. */ public static SVNProperties unmodifiableProperties(SVNProperties properties) { Map propertiesMap = properties.myProperties; propertiesMap = Collections.unmodifiableMap(propertiesMap); return new SVNProperties(propertiesMap); } /** * Creates an empty <code>SVNProperties</code> object. */ public SVNProperties() { myProperties = new SVNHashMap(); } /** * Creates a new <code>SVNProperties</code> object copying the given one. * * @param properties * an initializer */ public SVNProperties(SVNProperties properties) { myProperties = new SVNHashMap(properties.myProperties); } private SVNProperties(Map properties) { myProperties = properties; } /** * Returns SVNProperties as Map of String, SVNPropertyValue pairs. * * @return copy of SVNProperties as Map object */ public Map<String, SVNPropertyValue> asMap() { if (myProperties == null) { return Collections.unmodifiableMap(Collections.EMPTY_MAP); } return Collections.unmodifiableMap(myProperties); } /** * Stores a new mapping <code>propertyName</code> to * <code>propertyValue</code> in this object. * * @param propertyName * property name * @param propertyValue * property value object */ public void put(String propertyName, SVNPropertyValue propertyValue) { myProperties.put(propertyName, propertyValue); } /** * Stores a new property name-to-value mapping in this object. * * <p> * <code>propertyValue</code> is converted to an {@link SVNPropertyValue} * object through a call to {@link SVNPropertyValue#create(String)}. * * @param propertyName * property name * @param propertyValue * property value string */ public void put(String propertyName, String propertyValue) { myProperties.put(propertyName, SVNPropertyValue.create(propertyValue)); } /** * Stores a new property name-to-value mapping in this object. * * <p> * <code>propertyValue</code> is converted to an {@link SVNPropertyValue} * object through a call to {@link SVNPropertyValue#create(String)}. * * @param propertyName * property name * @param propertyValue * property value string */ public void put(String propertyName, char[] propertyValue, String encoding) { myProperties.put(propertyName, SVNPropertyValue.create(propertyValue, encoding)); } /** * Stores a new property name-to-value mapping in this object. * * <p> * <code>propertyValue</code> is converted to an {@link SVNPropertyValue} * object through a call to {@link SVNPropertyValue#create(String, byte[])}. * * @param propertyName * property name * @param propertyValue * property value bytes */ public void put(String propertyName, byte[] propertyValue) { myProperties.put(propertyName, SVNPropertyValue.create(propertyName, propertyValue)); } /** * Returns a <code>String</code> property value. * * @param propertyName * property name * @return property value string; <span class="javakeyword">null</span> if * there's no such property or if it's not a <code>String</code> * property value */ public String getStringValue(String propertyName) { SVNPropertyValue value = (SVNPropertyValue) myProperties.get(propertyName); return value == null ? null : value.getString(); } /** * Returns a binary property value. * * @param propertyName * property name * @return byte array containing property value bytes; <span * class="javakeyword">null</span> if there's no such property or if * it's not a binary property value */ public byte[] getBinaryValue(String propertyName) { SVNPropertyValue value = (SVNPropertyValue) myProperties.get(propertyName); return value == null ? null : value.getBytes(); } /** * Returns a property value as an {@link SVNPropertyValue}. * * @param propertyName * property name * @return property value object; <span class="javakeyword">null</span> if * there's no such property */ public SVNPropertyValue getSVNPropertyValue(String propertyName) { return (SVNPropertyValue) myProperties.get(propertyName); } /** * Removes the specified property from this properties object. * * @param propertyName * name of the property to remove from this object * @return the value of the removed object */ public SVNPropertyValue remove(String propertyName) { return (SVNPropertyValue) myProperties.remove(propertyName); } /** * Puts all properties from the specified properties object to this object. * * @param properties * properties object */ public void putAll(SVNProperties properties) { myProperties.putAll(properties.myProperties); } /** * Tells if this properties object holds no properties (empty). * * @return <span class="javakeyword">true</span> if this object holds no * properties; otherwise <span class="javakeyword">false</span> */ public boolean isEmpty() { return myProperties.isEmpty(); } /** * Removes all properties from this object. * */ public void clear() { myProperties.clear(); } /** * Removes all mappings which values are <span * class="javakeyword">null</span>s from this object. */ public void removeNullValues() { for (Iterator iterator = myProperties.keySet().iterator(); iterator.hasNext();) { String name = (String) iterator.next(); if (myProperties.get(name) == null) { iterator.remove(); } } } /** * Returns the number of properties held by this object. * * @return number of properties */ public int size() { return myProperties.size(); } /** * Tells whether this properties object contains the specified property * name. * * @param propertyName * property name * @return <span class="javakeyword">true</span> if this object contains a * mapping with the specified key (<code>propertyName</code>) */ public boolean containsName(String propertyName) { return myProperties.containsKey(propertyName); } /** * Returns a set of property names contained by this object. * * @return property names set */ public Set<String> nameSet() { return myProperties.keySet(); } /** * Tells whether this properties object contains the specified property * value. * * @param value * property value * @return <span class="javakeyword">true</span> if this object contains * <code>value</code> * */ public boolean containsValue(SVNPropertyValue value) { return myProperties.containsValue(value); } /** * Returns a collection of property values contained in this properties * object. * * @return property values collection */ public Collection values() { return myProperties.values(); } /** * Returns a subset of properties contained in this properties object which * suffice for {@link SVNProperty#isRegularProperty(String)} clause. * * @return regular properties; if there are no properties which would * suffice the aforementioned clause, an empty * <code>SVNProperties</code> object is returned */ public SVNProperties getRegularProperties() { SVNProperties result = new SVNProperties(); for (Iterator propNamesIter = nameSet().iterator(); propNamesIter.hasNext();) { String propName = (String) propNamesIter.next(); if (SVNProperty.isRegularProperty(propName)) { result.put(propName, getSVNPropertyValue(propName)); } } return result; } /** * Compares this object against another one returning a difference between * them. * * <p/> * Properties which are present in this object but are not in * <code>properties</code>, are put to the result as property name to <span * class="javakeyword">null</span> mappings. Properties which are present * only in <code>properties</code> but not in this object, are added to the * result. Also result will include those properties which are present in * both objects but have different values; in this case result will include * such properties with values from <code>properties</code>. * * @param properties * another properties object * @return properties object holding the properties difference */ public SVNProperties compareTo(SVNProperties properties) { SVNProperties result = new SVNProperties(); if (isEmpty()) { result.putAll(properties); return result; } Collection props1 = nameSet(); Collection props2 = properties.nameSet(); // missed in props2. Collection tmp = new TreeSet(props1); tmp.removeAll(props2); for (Iterator props = tmp.iterator(); props.hasNext();) { String missing = (String) props.next(); result.put(missing, (byte[]) null); } // added in props2. tmp = new TreeSet(props2); tmp.removeAll(props1); for (Iterator props = tmp.iterator(); props.hasNext();) { String added = (String) props.next(); result.put(added, properties.getSVNPropertyValue(added)); } // changed in props2 tmp = new TreeSet(props2); tmp.retainAll(props1); for (Iterator props = tmp.iterator(); props.hasNext();) { String changed = (String) props.next(); SVNPropertyValue value1 = getSVNPropertyValue(changed); SVNPropertyValue value2 = properties.getSVNPropertyValue(changed); if (!SVNPropertyValue.areEqual(value1, value2)) { result.put(changed, value2); } } return result; } /** * Returns a hash code of this object. * * <p/> * A hash code is evaluated as follows: <code>31 + </code> * {@link java.util.Map#hashCode() hash code} of the underlying * <code>Map</code> holding the property key to property value mappings. * * @return hash code of this object */ public int hashCode() { return 31 + ((myProperties == null) ? 0 : myProperties.hashCode()); } /** * Tells whether this object and <code>obj</code> are equal. * * @param obj * object to compare with * @return <span class="javakeyword">true</span> if <code>obj</code> is * either this very object, or is an instance of * <code>SVNProperties</code> with the same contents of properties * */ public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } SVNProperties other = (SVNProperties) obj; if (myProperties == null) { if (other.myProperties != null) { return false; } } else if (!myProperties.equals(other.myProperties)) { return false; } return true; } /** * Creates and returns a copy of this object. * * @return a clone of this instance * */ public Object clone() throws CloneNotSupportedException { try { super.clone(); } catch (CloneNotSupportedException cnse) { return null; } SVNProperties result = new SVNProperties(); result.putAll(this); return result; } public String toString() { if (myProperties != null) { return myProperties.toString(); } return ""; } public void dispose() { if (myProperties != null) { for (SVNPropertyValue value : myProperties.values()) { value.clear(); } } } }