2006-03-03 16:57:55 +00:00
/ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2006-12-22 19:18:16 +00:00
Licensed to the Apache Software Foundation ( ASF ) under one or more
contributor license agreements . See the NOTICE file distributed with
this work for additional information regarding copyright ownership .
The ASF licenses this file to You under the Apache License , Version 2 . 0
( the " License " ) ; you may not use this file except in compliance with
the License . You may obtain a copy of the License at
2006-03-03 16:57:55 +00:00
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an " AS IS " BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * /
package org.apache.poi.hpsf ;
2017-05-02 23:24:50 +00:00
import java.io.UnsupportedEncodingException ;
2017-05-03 19:18:45 +00:00
import java.math.BigInteger ;
2017-05-02 23:24:50 +00:00
import java.nio.charset.Charset ;
2017-05-03 19:18:45 +00:00
import java.util.ArrayList ;
import java.util.Collection ;
import java.util.Collections ;
2006-03-03 16:57:55 +00:00
import java.util.Date ;
import java.util.HashMap ;
2017-05-03 19:18:45 +00:00
import java.util.LinkedHashMap ;
import java.util.List ;
2006-03-03 16:57:55 +00:00
import java.util.Map ;
2008-08-12 18:44:50 +00:00
import java.util.Set ;
2006-03-03 16:57:55 +00:00
2016-11-27 20:19:18 +00:00
import org.apache.commons.collections4.bidimap.TreeBidiMap ;
2006-03-03 16:57:55 +00:00
import org.apache.poi.hpsf.wellknown.PropertyIDMap ;
2017-05-02 23:24:50 +00:00
import org.apache.poi.util.CodePageUtil ;
import org.apache.poi.util.POILogFactory ;
import org.apache.poi.util.POILogger ;
2006-03-03 16:57:55 +00:00
/ * *
2016-11-27 20:19:18 +00:00
* Maintains the instances of { @link CustomProperty } that belong to a
2006-03-03 16:57:55 +00:00
* { @link DocumentSummaryInformation } . The class maintains the names of the
* custom properties in a dictionary . It implements the { @link Map } interface
* and by this provides a simplified view on custom properties : A property ' s
* name is the key that maps to a typed value . This implementation hides
* property IDs from the developer and regards the property names as keys to
2016-11-27 20:19:18 +00:00
* typed values . < p >
2009-10-08 22:29:41 +00:00
*
2016-11-27 20:19:18 +00:00
* While this class provides a simple API to custom properties , it ignores
2006-03-03 16:57:55 +00:00
* the fact that not names , but IDs are the real keys to properties . Under the
* hood this class maintains a 1 : 1 relationship between IDs and names . Therefore
* you should not use this class to process property sets with several IDs
* mapping to the same name or with properties without a name : the result will
* contain only a subset of the original properties . If you really need to deal
2016-11-27 20:19:18 +00:00
* such property sets , use HPSF ' s low - level access methods . < p >
2009-10-08 22:29:41 +00:00
*
2016-11-27 20:19:18 +00:00
* An application can call the { @link # isPure } method to check whether a
2006-03-03 16:57:55 +00:00
* property set parsed by { @link CustomProperties } is still pure ( i . e .
2016-11-27 20:19:18 +00:00
* unmodified ) or whether one or more properties have been dropped . < p >
2009-10-08 22:29:41 +00:00
*
2016-11-27 20:19:18 +00:00
* This class is not thread - safe ; concurrent access to instances of this
* class must be synchronized . < p >
2009-10-08 22:29:41 +00:00
*
2016-11-27 20:19:18 +00:00
* While this class is roughly HashMap & lt ; Long , CustomProperty & gt ; , that ' s the
* internal representation . To external calls , it should appear as
* HashMap & lt ; String , Object & gt ; mapping between Names and Custom Property Values .
2006-03-03 16:57:55 +00:00
* /
2017-05-03 19:18:45 +00:00
public class CustomProperties implements Map < String , Object > {
2017-05-02 23:24:50 +00:00
private static final POILogger LOG = POILogFactory . getLogger ( CustomProperties . class ) ;
2017-05-03 19:18:45 +00:00
/ * *
* The custom properties
* /
private final HashMap < Long , CustomProperty > props = new HashMap < Long , CustomProperty > ( ) ;
2006-03-03 16:57:55 +00:00
/ * *
2016-11-27 20:19:18 +00:00
* Maps property IDs to property names and vice versa .
2006-03-03 16:57:55 +00:00
* /
2016-11-27 20:19:18 +00:00
private final TreeBidiMap < Long , String > dictionary = new TreeBidiMap < Long , String > ( ) ;
2006-03-03 16:57:55 +00:00
/ * *
2016-11-27 20:19:18 +00:00
* Tells whether this object is pure or not .
2006-03-03 16:57:55 +00:00
* /
private boolean isPure = true ;
2017-05-02 23:24:50 +00:00
private int codepage = - 1 ;
2006-03-03 16:57:55 +00:00
/ * *
2016-11-27 20:19:18 +00:00
* Puts a { @link CustomProperty } into this map . It is assumed that the
2006-03-03 16:57:55 +00:00
* { @link CustomProperty } already has a valid ID . Otherwise use
2016-11-27 20:19:18 +00:00
* { @link # put ( CustomProperty ) } .
2016-07-03 18:25:05 +00:00
*
* @param name the property name
* @param cp the property
*
* @return the previous property stored under this name
2006-03-03 16:57:55 +00:00
* /
2016-11-27 20:19:18 +00:00
public CustomProperty put ( final String name , final CustomProperty cp ) {
if ( name = = null ) {
2006-03-03 16:57:55 +00:00
/* Ignoring a property without a name. */
isPure = false ;
return null ;
}
2016-11-27 20:19:18 +00:00
if ( ! name . equals ( cp . getName ( ) ) ) {
2006-03-03 16:57:55 +00:00
throw new IllegalArgumentException ( " Parameter \" name \" ( " + name +
" ) and custom property's name ( " + cp . getName ( ) +
" ) do not match. " ) ;
2016-11-27 20:19:18 +00:00
}
2006-03-03 16:57:55 +00:00
2017-05-02 23:24:50 +00:00
checkCodePage ( name ) ;
2006-03-03 16:57:55 +00:00
/* Register name and ID in the dictionary. Mapping in both directions is possible. If there is already a */
2017-05-03 19:18:45 +00:00
props . remove ( dictionary . getKey ( name ) ) ;
2016-11-27 20:19:18 +00:00
dictionary . put ( cp . getID ( ) , name ) ;
2006-03-03 16:57:55 +00:00
/* Put the custom property into this map. */
2017-05-03 19:18:45 +00:00
return props . put ( cp . getID ( ) , cp ) ;
2006-03-03 16:57:55 +00:00
}
/ * *
2017-05-03 19:18:45 +00:00
* Adds a named property .
2009-10-08 22:29:41 +00:00
*
2017-05-03 19:18:45 +00:00
* @param key The property ' s name .
* @param value The property ' s value .
* @return the property that was stored under the specified name before , or
* { @code null } if there was no such property before .
2006-03-03 16:57:55 +00:00
* /
2017-05-03 19:18:45 +00:00
@Override
public Object put ( String key , Object value ) {
int variantType ;
if ( value instanceof String ) {
variantType = Variant . VT_LPSTR ;
} else if ( value instanceof Short ) {
variantType = Variant . VT_I2 ;
} else if ( value instanceof Integer ) {
variantType = Variant . VT_I4 ;
} else if ( value instanceof Long ) {
variantType = Variant . VT_I8 ;
} else if ( value instanceof Float ) {
variantType = Variant . VT_R4 ;
} else if ( value instanceof Double ) {
variantType = Variant . VT_R8 ;
} else if ( value instanceof Boolean ) {
variantType = Variant . VT_BOOL ;
} else if ( value instanceof BigInteger
& & ( ( BigInteger ) value ) . bitLength ( ) < = 64
& & ( ( BigInteger ) value ) . compareTo ( BigInteger . ZERO ) > = 0 ) {
variantType = Variant . VT_UI8 ;
} else if ( value instanceof Date ) {
variantType = Variant . VT_FILETIME ;
2016-11-27 20:19:18 +00:00
} else {
2017-05-03 19:18:45 +00:00
throw new IllegalStateException ( " unsupported datatype - currently String,Short,Integer,Long,Float,Double,Boolean,BigInteger(unsigned long),Date can be processed. " ) ;
2006-03-03 16:57:55 +00:00
}
2017-09-14 23:44:47 +00:00
final Property p = new Property ( - 1 , variantType , value ) ;
2017-05-03 19:18:45 +00:00
return put ( new CustomProperty ( p , key ) ) ;
2006-03-03 16:57:55 +00:00
}
2017-05-03 19:18:45 +00:00
2006-03-03 16:57:55 +00:00
/ * *
2017-05-03 19:18:45 +00:00
* Gets a named value from the custom properties - only works for keys of type String
2006-03-03 16:57:55 +00:00
*
2017-05-03 19:18:45 +00:00
* @param key the name of the value to get
* @return the value or { @code null } if a value with the specified
* name is not found in the custom properties .
2006-03-03 16:57:55 +00:00
* /
2017-05-03 19:18:45 +00:00
@Override
public Object get ( final Object key ) {
final Long id = dictionary . getKey ( key ) ;
final CustomProperty cp = props . get ( id ) ;
return cp ! = null ? cp . getValue ( ) : null ;
2006-03-03 16:57:55 +00:00
}
/ * *
2017-05-03 19:18:45 +00:00
* Removes a custom property - only works for keys of type String
* @param key The name of the custom property to remove
* @return The removed property or { @code null } if the specified property was not found .
2006-03-03 16:57:55 +00:00
* /
2017-05-03 19:18:45 +00:00
@Override
public CustomProperty remove ( Object key ) {
final Long id = dictionary . removeValue ( key ) ;
return props . remove ( id ) ;
2006-03-03 16:57:55 +00:00
}
2017-05-03 19:18:45 +00:00
@Override
public int size ( ) {
return props . size ( ) ;
2006-03-03 16:57:55 +00:00
}
2017-05-03 19:18:45 +00:00
@Override
public boolean isEmpty ( ) {
return props . isEmpty ( ) ;
2006-03-03 16:57:55 +00:00
}
2017-05-03 19:18:45 +00:00
@Override
public void clear ( ) {
props . clear ( ) ;
}
@Override
public int hashCode ( ) {
return props . hashCode ( ) ;
2006-03-03 16:57:55 +00:00
}
2017-05-03 19:18:45 +00:00
@Override
public boolean equals ( Object obj ) {
if ( ! ( obj instanceof CustomProperties ) ) {
return false ;
}
return props . equals ( ( ( CustomProperties ) obj ) . props ) ;
2006-03-03 16:57:55 +00:00
}
2017-05-03 19:18:45 +00:00
@Override
public void putAll ( Map < ? extends String , ? extends Object > m ) {
for ( Map . Entry < ? extends String , ? extends Object > me : m . entrySet ( ) ) {
put ( me . getKey ( ) , me . getValue ( ) ) ;
}
}
2009-10-08 22:29:41 +00:00
2006-03-03 16:57:55 +00:00
/ * *
2017-05-03 19:18:45 +00:00
* @return the list of properties
2006-03-03 16:57:55 +00:00
* /
2017-05-03 19:18:45 +00:00
public List < CustomProperty > properties ( ) {
List < CustomProperty > list = new ArrayList < CustomProperty > ( props . size ( ) ) ;
for ( Long l : dictionary . keySet ( ) ) {
list . add ( props . get ( l ) ) ;
}
return Collections . unmodifiableList ( list ) ;
2006-03-03 16:57:55 +00:00
}
2017-05-03 19:18:45 +00:00
2006-03-03 16:57:55 +00:00
/ * *
2017-05-03 19:18:45 +00:00
* @return the list of property values - use { @link # properties ( ) } for the wrapped values
2006-03-03 16:57:55 +00:00
* /
2017-05-03 19:18:45 +00:00
@Override
public Collection < Object > values ( ) {
List < Object > list = new ArrayList < Object > ( props . size ( ) ) ;
for ( Long l : dictionary . keySet ( ) ) {
list . add ( props . get ( l ) . getValue ( ) ) ;
}
return Collections . unmodifiableCollection ( list ) ;
}
@Override
public Set < Entry < String , Object > > entrySet ( ) {
Map < String , Object > set = new LinkedHashMap < String , Object > ( props . size ( ) ) ;
for ( Entry < Long , String > se : dictionary . entrySet ( ) ) {
set . put ( se . getValue ( ) , props . get ( se . getKey ( ) ) . getValue ( ) ) ;
}
return Collections . unmodifiableSet ( set . entrySet ( ) ) ;
2006-03-03 16:57:55 +00:00
}
2009-10-08 22:29:41 +00:00
2006-03-03 16:57:55 +00:00
/ * *
2016-07-03 18:25:05 +00:00
* Returns a set of all the names of our custom properties .
* Equivalent to { @link # nameSet ( ) }
*
* @return a set of all the names of our custom properties
2008-08-12 18:44:50 +00:00
* /
2016-07-03 18:25:05 +00:00
@Override
@SuppressWarnings ( { " rawtypes " , " unchecked " } )
2008-08-12 18:44:50 +00:00
public Set keySet ( ) {
2017-05-03 19:18:45 +00:00
return Collections . unmodifiableSet ( dictionary . values ( ) ) ;
2009-10-08 22:29:41 +00:00
}
2008-08-12 18:44:50 +00:00
2010-01-13 15:42:53 +00:00
/ * *
2016-07-03 18:25:05 +00:00
* Returns a set of all the names of our custom properties
*
* @return a set of all the names of our custom properties
2010-01-13 15:42:53 +00:00
* /
public Set < String > nameSet ( ) {
2017-05-03 19:18:45 +00:00
return Collections . unmodifiableSet ( dictionary . values ( ) ) ;
2010-01-13 15:42:53 +00:00
}
/ * *
2016-07-03 18:25:05 +00:00
* Returns a set of all the IDs of our custom properties
*
* @return a set of all the IDs of our custom properties
2010-01-13 15:42:53 +00:00
* /
2017-05-03 19:18:45 +00:00
public Set < Long > idSet ( ) {
return Collections . unmodifiableSet ( dictionary . keySet ( ) ) ;
2010-01-13 15:42:53 +00:00
}
2008-08-12 18:44:50 +00:00
2009-10-08 22:29:41 +00:00
/ * *
2016-11-27 20:19:18 +00:00
* Sets the codepage .
2006-03-03 16:57:55 +00:00
*
* @param codepage the codepage
* /
2016-11-27 20:19:18 +00:00
public void setCodepage ( final int codepage ) {
2017-05-02 23:24:50 +00:00
this . codepage = codepage ;
2006-03-03 16:57:55 +00:00
}
2017-05-02 23:24:50 +00:00
/ * *
* Gets the codepage .
*
* @return the codepage or - 1 if the codepage is undefined .
* /
public int getCodepage ( ) {
return codepage ;
}
2006-03-03 16:57:55 +00:00
/ * *
* < p > Gets the dictionary which contains IDs and names of the named custom
* properties .
2009-10-08 22:29:41 +00:00
*
2006-03-03 16:57:55 +00:00
* @return the dictionary .
* /
2016-11-27 20:19:18 +00:00
Map < Long , String > getDictionary ( ) {
return dictionary ;
2006-03-03 16:57:55 +00:00
}
/ * *
2010-01-13 15:42:53 +00:00
* Checks against both String Name and Long ID
* /
2016-11-27 20:19:18 +00:00
@Override
public boolean containsKey ( Object key ) {
return ( ( key instanceof Long & & dictionary . containsKey ( key ) ) | | dictionary . containsValue ( key ) ) ;
}
/ * *
* Checks against both the property , and its values .
* /
@Override
public boolean containsValue ( Object value ) {
if ( value instanceof CustomProperty ) {
2017-05-03 19:18:45 +00:00
return props . containsValue ( value ) ;
2016-11-27 20:19:18 +00:00
}
2017-05-03 19:18:45 +00:00
for ( CustomProperty cp : props . values ( ) ) {
2010-01-13 15:42:53 +00:00
if ( cp . getValue ( ) = = value ) {
2016-11-27 20:19:18 +00:00
return true ;
2010-01-13 15:42:53 +00:00
}
2016-11-27 20:19:18 +00:00
}
2010-01-13 15:42:53 +00:00
2016-11-27 20:19:18 +00:00
return false ;
}
2010-01-13 15:42:53 +00:00
2006-03-03 16:57:55 +00:00
/ * *
2016-11-27 20:19:18 +00:00
* Tells whether this { @link CustomProperties } instance is pure or one or
2006-03-03 16:57:55 +00:00
* more properties of the underlying low - level property set has been
2016-11-27 20:19:18 +00:00
* dropped .
2009-10-08 22:29:41 +00:00
*
2016-11-27 20:19:18 +00:00
* @return { @code true } if the { @link CustomProperties } is pure , else
* { @code false } .
2006-03-03 16:57:55 +00:00
* /
2016-11-27 20:19:18 +00:00
public boolean isPure ( ) {
2006-03-03 16:57:55 +00:00
return isPure ;
}
/ * *
2016-11-27 20:19:18 +00:00
* Sets the purity of the custom property set .
2006-03-03 16:57:55 +00:00
*
* @param isPure the purity
* /
2016-11-27 20:19:18 +00:00
public void setPure ( final boolean isPure ) {
2006-03-03 16:57:55 +00:00
this . isPure = isPure ;
}
2017-05-03 19:18:45 +00:00
/ * *
* Puts a { @link CustomProperty } that has not yet a valid ID into this
* map . The method will allocate a suitable ID for the custom property :
*
* < ul >
* < li > If there is already a property with the same name , take the ID
* of that property .
*
* < li > Otherwise find the highest ID and use its value plus one .
* < / ul >
*
* @param customProperty
* @return If there was already a property with the same name , the old property
* @throws ClassCastException
* /
private Object put ( final CustomProperty customProperty ) throws ClassCastException {
final String name = customProperty . getName ( ) ;
/* Check whether a property with this name is in the map already. */
final Long oldId = ( name = = null ) ? null : dictionary . getKey ( name ) ;
if ( oldId ! = null ) {
customProperty . setID ( oldId ) ;
} else {
long lastKey = ( dictionary . isEmpty ( ) ) ? 0 : dictionary . lastKey ( ) ;
long nextKey = Math . max ( lastKey , PropertyIDMap . PID_MAX ) + 1 ;
customProperty . setID ( nextKey ) ;
}
return this . put ( name , customProperty ) ;
}
2017-05-02 23:24:50 +00:00
private void checkCodePage ( String value ) {
int cp = getCodepage ( ) ;
if ( cp = = - 1 ) {
cp = Property . DEFAULT_CODEPAGE ;
}
if ( cp = = CodePageUtil . CP_UNICODE ) {
return ;
}
String cps = " " ;
try {
cps = CodePageUtil . codepageToEncoding ( cp , false ) ;
} catch ( UnsupportedEncodingException e ) {
LOG . log ( POILogger . ERROR , " Codepage ' " + cp + " ' can't be found. " ) ;
}
if ( ! cps . isEmpty ( ) & & Charset . forName ( cps ) . newEncoder ( ) . canEncode ( value ) ) {
return ;
}
LOG . log ( POILogger . DEBUG , " Charset ' " + cps + " ' can't encode ' " + value + " ' - switching to unicode. " ) ;
setCodepage ( CodePageUtil . CP_UNICODE ) ;
}
2006-03-03 16:57:55 +00:00
}