/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat, Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.search.util.configuration.impl; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.InvalidPropertiesFormatException; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import org.hibernate.search.util.logging.impl.Log; import org.hibernate.search.util.logging.impl.LoggerFactory; /** * A wrapper to Properties, to restrict the availability of * values to only those which have a key beginning with some * masking String. * Supported methods to enumerate the list of properties are: * - propertyNames() * - keySet() * - keys() * Other methods including methods returning Entries and values are not supported * * @author Sanne Grinovero * @author Emmanuel Bernard */ public class MaskedProperty extends Properties implements Serializable { private static final long serialVersionUID = -593307257383085113L; private static final Log log = LoggerFactory.make(); private final Properties masked; private final Properties fallBack; private final String radix; private transient Set<Object> propertyNames; /** * Provides a view to the provided Properties hiding * all keys not starting with some [mask.]. * * @param propsToMask the Properties containing the values. * @param mask a {@link java.lang.String} object. */ public MaskedProperty(Properties propsToMask, String mask) { this( propsToMask, mask, null ); } /** * Provides a view to the provided Properties hiding * all keys not starting with some [mask.]. * If no value is found then a value is returned from propsFallBack, * without masking. * @param propsToMask * @param mask * @param propsFallBack */ public MaskedProperty(Properties propsToMask, String mask, Properties propsFallBack) { if ( propsToMask==null || mask==null ) { throw new java.lang.IllegalArgumentException(); } this.masked = propsToMask; this.radix = mask + "."; this.fallBack = propsFallBack; } @Override public String getProperty(String key) { String compositeKey = radix + key; String value = masked.getProperty( compositeKey ); if ( value != null) { log.tracef( "found a match for key: [%s] value: %s", compositeKey, value ); return value; } else if ( fallBack != null ) { return fallBack.getProperty( key ); } else { return null; } } /** * @throws IllegalArgumentException if the key is not a String instance */ @Override public synchronized boolean containsKey(Object key) { if ( ! ( key instanceof String ) ) { throw new IllegalArgumentException( "key must be a String" ); } return getProperty( key.toString() ) != null; } @Override public String getProperty(String key, String defaultValue) { String val = getProperty( key ); return ( val == null ) ? defaultValue : val; } /** * @throws UnsupportedOperationException */ @Override public void list(PrintStream out) { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public void list(PrintWriter out) { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public void load(InputStream inStream) throws IOException { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException { throw new UnsupportedOperationException(); } @Override public Enumeration<?> propertyNames() { initPropertyNames(); return Collections.enumeration( propertyNames ); } private synchronized void initPropertyNames() { if ( propertyNames != null) return; Set<Object> maskedProperties = new TreeSet<Object>(); //we use keys to be safe and avoid CCE for non String key entries Enumeration<?> maskedNames = masked.propertyNames(); while ( maskedNames.hasMoreElements() ) { Object key = maskedNames.nextElement(); if ( String.class.isInstance( key ) ) { String maskedProperty = (String) key; if ( maskedProperty.startsWith( radix ) ) { maskedProperties.add(maskedProperty.substring( radix.length(), maskedProperty.length() ) ); } } } if ( fallBack != null ) { Enumeration<?> fallBackNames = fallBack.propertyNames(); while ( fallBackNames.hasMoreElements() ) { Object key = fallBackNames.nextElement(); if ( String.class.isInstance( key ) ) { maskedProperties.add( key ); } } } propertyNames = Collections.unmodifiableSet( maskedProperties ); } /** * @throws UnsupportedOperationException */ @Override public void save(OutputStream out, String comments) { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public Object setProperty(String key, String value) { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public void store(OutputStream out, String comments) throws IOException { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public void storeToXML(OutputStream os, String comment, String encoding) throws IOException { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public void storeToXML(OutputStream os, String comment) throws IOException { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public void clear() { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public Object clone() { throw new UnsupportedOperationException(); } @Override public synchronized boolean contains(Object value) { initPropertyNames(); return propertyNames.contains( value ); } /** * @throws UnsupportedOperationException */ @Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public Enumeration<Object> elements() { //TODO throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public Set<java.util.Map.Entry<Object, Object>> entrySet() { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public Object get(Object key) { throw new UnsupportedOperationException(); } @Override public synchronized boolean isEmpty() { initPropertyNames(); return propertyNames.isEmpty(); } /** * @throws UnsupportedOperationException */ @Override public synchronized Enumeration<Object> keys() { initPropertyNames(); return Collections.enumeration( propertyNames ); } @Override public Set<Object> keySet() { initPropertyNames(); return propertyNames; } /** * @throws UnsupportedOperationException */ @Override public Object put(Object key, Object value) { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public void putAll(Map<? extends Object, ? extends Object> t) { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override protected void rehash() { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public Object remove(Object key) { throw new UnsupportedOperationException(); } /** * @throws UnsupportedOperationException */ @Override public synchronized int size() { initPropertyNames(); return propertyNames.size(); } @Override public synchronized String toString() { HashMap fake = new HashMap(); Enumeration<?> names = propertyNames(); while ( names.hasMoreElements() ) { Object nextElement = names.nextElement(); fake.put( nextElement, this.getProperty( nextElement.toString() ) ); } return fake.toString(); } /** * @throws UnsupportedOperationException */ @Override public Collection<Object> values() { throw new UnsupportedOperationException(); } @Override public synchronized int hashCode() { final int prime = 31; int result = ( ( fallBack == null ) ? 0 : fallBack.hashCode() ); result = prime * result + masked.hashCode(); result = prime * result + radix.hashCode(); return result; } @Override public synchronized boolean equals(Object obj) { if ( this == obj ) return true; if ( obj == null ) return false; if ( getClass() != obj.getClass() ) return false; final MaskedProperty other = (MaskedProperty) obj; if ( fallBack == null ) { if ( other.fallBack != null ) return false; } else if ( ! fallBack.equals( other.fallBack ) ) return false; if ( ! masked.equals( other.masked ) ) return false; if ( ! radix.equals( other.radix ) ) return false; return true; } }