/*******************************************************************************
* Copyright 2010 Atos Worldline SAS
*
* Licensed by Atos Worldline SAS under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Atos Worldline SAS 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
*
* 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 net.padaf.xmpbox.parser;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import net.padaf.xmpbox.XMPMetadata;
import net.padaf.xmpbox.schema.AdobePDFSchema;
import net.padaf.xmpbox.schema.DublinCoreSchema;
import net.padaf.xmpbox.schema.PDFAExtensionSchema;
import net.padaf.xmpbox.schema.PDFAFieldDescription;
import net.padaf.xmpbox.schema.PDFAIdentificationSchema;
import net.padaf.xmpbox.schema.PDFAPropertyDescription;
import net.padaf.xmpbox.schema.PDFAValueTypeDescription;
import net.padaf.xmpbox.schema.PropertyAttributesAnnotation;
import net.padaf.xmpbox.schema.PropertyType;
import net.padaf.xmpbox.schema.SchemaDescription;
import net.padaf.xmpbox.schema.XMPBasicSchema;
import net.padaf.xmpbox.schema.XMPMediaManagementSchema;
import net.padaf.xmpbox.schema.XMPRightsManagementSchema;
import net.padaf.xmpbox.schema.XMPSchema;
/**
* Retrieve information about schemas
*
* @author a183132
*
*/
public class NSMapping {
public static final List<String> BASIC_TYPES;
public static final HashMap<String, String> COMPLEX_BASIC_TYPES;
static {
BASIC_TYPES = new ArrayList<String>();
BASIC_TYPES.add("Text");
BASIC_TYPES.add("Integer");
BASIC_TYPES.add("Boolean");
BASIC_TYPES.add("Date");
BASIC_TYPES.add("URI");
BASIC_TYPES.add("URL");
BASIC_TYPES.add("bag Text");
BASIC_TYPES.add("bag ProperName");
BASIC_TYPES.add("bag Job");
BASIC_TYPES.add("bag Xpath");
BASIC_TYPES.add("seq Text");
BASIC_TYPES.add("seq Field");
BASIC_TYPES.add("seq Date");
BASIC_TYPES.add("Lang Alt");
COMPLEX_BASIC_TYPES = new HashMap<String, String>();
COMPLEX_BASIC_TYPES.put("http://ns.adobe.com/xap/1.0/g/img/",
"Thumbnail");
}
protected Map<String, XMPSchemaFactory> nsMaps;
protected Map<String, String> complexBasicTypesDeclarationEntireXMPLevel;
protected Map<String, String> complexBasicTypesDeclarationSchemaLevel;
protected Map<String, String> complexBasicTypesDeclarationPropertyLevel;
/**
* Constructor of the NameSpace mapping
*
* @throws XmpSchemaException
* When could not read a property data in a Schema Class given
*/
public NSMapping() throws XmpSchemaException {
nsMaps = new HashMap<String, XMPSchemaFactory>();
complexBasicTypesDeclarationEntireXMPLevel = new HashMap<String, String>();
complexBasicTypesDeclarationSchemaLevel = new HashMap<String, String>();
complexBasicTypesDeclarationPropertyLevel = new HashMap<String, String>();
init();
}
/**
* Add mapping of common schemas
*
* @throws XmpSchemaException
* When could not read a property data in a Schema Class given
*/
private void init() throws XmpSchemaException {
addNameSpace("http://ns.adobe.com/xap/1.0/", XMPBasicSchema.class);
addNameSpace("http://purl.org/dc/elements/1.1/", DublinCoreSchema.class);
addNameSpace("http://www.aiim.org/pdfa/ns/extension/", PDFAExtensionSchema.class);
addNameSpace("http://ns.adobe.com/xap/1.0/mm/", XMPMediaManagementSchema.class);
addNameSpace("http://ns.adobe.com/pdf/1.3/", AdobePDFSchema.class);
addNameSpace("http://www.aiim.org/pdfa/ns/id/", PDFAIdentificationSchema.class);
addNameSpace("http://ns.adobe.com/xap/1.0/rights/", XMPRightsManagementSchema.class);
}
/**
* Add a namespace declaration and Schema factory associated
*
* @param ns
* the Namespace URI
* @param classSchem
* The class representation of the schema linked to the namespace
* @throws XmpSchemaException
* When could not read property name in Schema Class given
*/
protected void addNameSpace(String ns, Class<? extends XMPSchema> classSchem)
throws XmpSchemaException {
nsMaps.put(ns, new XMPSchemaFactory(ns, classSchem, initializePropMapping(ns, classSchem)));
}
/**
* Initialize the Property Mapping for a given schema
*
* @param ns
* Namespace URI
* @param classSchem
* The class representation of the schema linked to the namespace
* @return Construct expected properties types representation
* @throws XmpSchemaException
* When could not read property name in field with properties
* annotations
*/
private PropMapping initializePropMapping(String ns,
Class<? extends XMPSchema> classSchem) throws XmpSchemaException {
PropertyType propType;
PropertyAttributesAnnotation propAtt;
Field[] fields;
PropMapping propMap = new PropMapping(ns);
fields = classSchem.getFields();
String propName = null;
for (Field field : fields) {
if (field.isAnnotationPresent(PropertyType.class)) {
try {
propName = (String) field.get(propName);
} catch (Exception e) {
throw new XmpSchemaException(
"couldn't read one type declaration, please check accessibility and declaration of fields annoted in "
+ classSchem.getName(), e.getCause());
}
// System.out.println("nameField:"+propName);
propType = field.getAnnotation(PropertyType.class);
// System.out.println("Type '"+propInfo.propertyType()+"' defined for "+propName);
if (!field
.isAnnotationPresent(PropertyAttributesAnnotation.class)) {
propMap.addNewProperty(propName, propType.propertyType(),
null);
} else {
// TODO Case where a special annotation is used to specify
// attributes
// NOT IMPLEMENTED YET, JUST TO GIVE A CLUE TO MAKE THIS
propAtt = field
.getAnnotation(PropertyAttributesAnnotation.class);
List<String> attributes = new ArrayList<String>();
for (String att : propAtt.expectedAttributes()) {
attributes.add(att);
}
propMap.addNewProperty(propName, propType.propertyType(),
attributes);
}
}
}
return propMap;
}
/**
* see if a specific type is known as a basic XMP type
*
* @param type
* Type to check
* @return True if type is a simple basic type
*/
private boolean isBasicType(String type) {
return BASIC_TYPES.contains(type);
}
/**
* Say if a specific namespace is known
*
* @param namespace
* The namespace URI checked
* @return True if namespace URI is known
*/
public boolean isContainedNamespace(String namespace) {
return nsMaps.containsKey(namespace);
}
/**
* Give type of specified property in specified schema (given by its
* namespaceURI)
*
* @param namespace
* The namespaceURI to explore
* @param prop
* the property Qualified Name
* @return Property type declared for namespace specified, null if unknown
*/
public String getSpecifiedPropertyType(String namespace, QName prop) {
if (nsMaps.containsKey(namespace)) {
return nsMaps.get(namespace).getPropertyType(prop.getLocalPart());
}
// check if its a complexbasicValueType and if it's has been declared
return getComplexBasicValueTypeEffectiveType(prop.getPrefix());
}
/**
* Check if a non basic value type used is describes in the schema which
* inlude a property with a such type
*
* @param desc
* The schema description associated to the schema which declare
* a property with specific value type
* @param definedValueType
* The value type name to find in value types descriptions
* @return The description of this specific value type
* @throws XmpUnknownValueTypeException
* If no declaration found
*/
private PDFAValueTypeDescription findValueTypeDescription(
SchemaDescription desc, String definedValueType)
throws XmpUnknownValueTypeException {
List<PDFAValueTypeDescription> values = desc.getValueTypes();
for (PDFAValueTypeDescription val : values) {
if (definedValueType.equals(val.getTypeNameValue())) {
return val;
}
}
throw new XmpUnknownValueTypeException("ValueType '" + definedValueType
+ "' is unknown. no declaration found in this schema");
}
/**
* Check if valueType used for a specified property description is known (in
* case where it's a normal value type or if a value type which has been
* defined in PDF/A Extension schema)
*
* @param desc
* The schema description associated to the schema which declare
* a property with specific value type
* @param definedValueType
* The value type name to find in value types descriptions
* @return value type equivalence (value type which can be treat (orginal
* basic value type or specific value type decomposed to find basic
* types)
* @throws XmpUnknownValueTypeException
* When Value Type is unknown
*
*/
private String getValueTypeEquivalence(SchemaDescription desc,
String definedValueType) throws XmpUnknownValueTypeException {
if (isBasicType(definedValueType)) {
return definedValueType;
}
PDFAValueTypeDescription val = findValueTypeDescription(desc,
definedValueType);
if (val.getFields().isEmpty()) {
// if fields value are note defined we suppose the property is a
// Text type
return "Text";
}
return "Field";
}
/**
* . For a specific valuetype declared in this schema. This method decompose
* it if field are present. and add types expected
*
* @param desc
* The schema description associated to the schema which declare
* a property with specific value type
* @param valueType
* valueType to analyze
* @param prop
* Expected properties types representation
* @throws XmpUnknownValueTypeException
* When a Value Type associated is unknown
*/
private void declareAssociatedFieldType(SchemaDescription desc,
String valueType, PropMapping prop)
throws XmpUnknownValueTypeException {
PDFAValueTypeDescription val = findValueTypeDescription(desc, valueType);
for (PDFAFieldDescription field : val.getFields()) {
// TODO case where a field call another nspace property ???
String fieldType = getValueTypeEquivalence(desc, field
.getValueTypeValue());
if (fieldType.equals("Field")) {
throw new XmpUnknownValueTypeException(
"ValueType Field reference a valuetype unknown");
}
prop.addNewProperty(field.getNameValue(), fieldType, null);
}
}
/**
* Add a new namespace Mapping for specific schema declared in PDF/A
* Extension schema
*
* @param desc
* The schemaDescription associated to the schema
* @throws XmpUnknownValueTypeException
* When a Value Type associated is unknown
*/
public void setNamespaceDefinition(SchemaDescription desc)
throws XmpUnknownValueTypeException {
PropMapping propMap = new PropMapping(desc.getNameSpaceURI());
List<PDFAPropertyDescription> props = desc.getProperties();
for (int i = 0; i < props.size(); i++) {
String type = getValueTypeEquivalence(desc, props.get(i).getValueTypeValue());
propMap.addNewProperty(props.get(i).getNameValue(), type, null);
if (type.equals("Field")) {
declareAssociatedFieldType(desc, props.get(i).getValueTypeValue(), propMap);
}
}
String nsName = desc.getPrefix();
String ns = desc.getNameSpaceURI();
nsMaps.put(ns, new XMPSchemaFactory(nsName, ns, XMPSchema.class, propMap));
}
/**
* Return the specialized schema class representation if it's known (create
* and add it to metadata). In other cases, return null
*
* @param metadata
* Metadata to link the new schema
* @param namespace
* The namespace URI
* @return Schema representation
* @throws XmpSchemaException
* When Instancing specified Object Schema failed
*/
public XMPSchema getAssociatedSchemaObject(XMPMetadata metadata, String namespace, String prefix) throws XmpSchemaException {
if (!nsMaps.containsKey(namespace)) {
return null;
}
XMPSchemaFactory factory = nsMaps.get(namespace);
return factory.createXMPSchema(metadata, prefix);
}
/**
* Check if a namespace used reference a complex basic types (like
* Thumbnails)
*
* @param namespace
* The namespace URI to check
* @return True if namespace URI is a reference for a complex basic type
*/
public boolean isComplexBasicTypes(String namespace) {
return COMPLEX_BASIC_TYPES.containsKey(namespace);
}
/**
* Check if a namespace declaration for a complex basic type has been found
* and if its valid for the entire XMP stream
*
* @param namespace
* the namespace URI
* @param prefix
* the prefix associated to this namespace
*/
public void setComplexBasicTypesDeclarationForLevelXMP(String namespace,
String prefix) {
if (isComplexBasicTypes(namespace)) {
complexBasicTypesDeclarationEntireXMPLevel.put(prefix, namespace);
}
}
/**
* Check if a namespace declaration for a complex basic type has been found
* and if its valid for the current schema description (at level of
* rdf:Description)
*
* @param namespace
* the namespace URI
* @param prefix
* the prefix associated to this namespace
*/
public void setComplexBasicTypesDeclarationForLevelSchema(String namespace,
String prefix) {
if (isComplexBasicTypes(namespace)) {
complexBasicTypesDeclarationSchemaLevel.put(prefix, namespace);
}
}
/**
* Check if a namespace declaration for a complex basic type has been found
* and if its valid for the current property description
*
* @param namespace
* the namespace URI
* @param prefix
* the prefix associated to this namespace
*/
public void setComplexBasicTypesDeclarationForLevelProperty(
String namespace, String prefix) {
if (isComplexBasicTypes(namespace)) {
complexBasicTypesDeclarationPropertyLevel.put(prefix, namespace);
}
}
/**
* Check for all XMP level if a complexBasicValueType prefix has been
* declared
*
* @param prefix
* The prefix which may design the namespace URI of the complex
* basic type
* @return The type if it is known, else null.
*/
public String getComplexBasicValueTypeEffectiveType(String prefix) {
if (complexBasicTypesDeclarationPropertyLevel.containsKey(prefix)) {
return COMPLEX_BASIC_TYPES
.get(complexBasicTypesDeclarationPropertyLevel.get(prefix));
}
if (complexBasicTypesDeclarationSchemaLevel.containsKey(prefix)) {
return COMPLEX_BASIC_TYPES
.get(complexBasicTypesDeclarationSchemaLevel.get(prefix));
}
if (complexBasicTypesDeclarationEntireXMPLevel.containsKey(prefix)) {
return COMPLEX_BASIC_TYPES
.get(complexBasicTypesDeclarationEntireXMPLevel.get(prefix));
}
return null;
}
/**
* Reset complex Basic types declaration for property level
*/
public void resetComplexBasicTypesDeclarationInPropertyLevel() {
complexBasicTypesDeclarationPropertyLevel.clear();
}
/**
* Reset complex Basic types declaration for schema level
*/
public void resetComplexBasicTypesDeclarationInSchemaLevel() {
complexBasicTypesDeclarationSchemaLevel.clear();
}
/**
* Reset complex Basic types declaration for Entire XMP level
*/
public void resetComplexBasicTypesDeclarationInEntireXMPLevel() {
complexBasicTypesDeclarationEntireXMPLevel.clear();
}
}