001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: XMPSchemaAdapter.java 426584 2006-07-28 16:01:47Z jeremias $ */
019:
020: package org.apache.xmlgraphics.xmp;
021:
022: import java.text.DateFormat;
023: import java.text.ParseException;
024: import java.text.SimpleDateFormat;
025: import java.util.Calendar;
026: import java.util.Date;
027: import java.util.TimeZone;
028:
029: import org.apache.xmlgraphics.util.QName;
030:
031: /**
032: * Base class for schema-specific adapters that provide user-friendly access to XMP values.
033: */
034: public class XMPSchemaAdapter {
035:
036: private static DateFormat pseudoISO8601DateFormat = new SimpleDateFormat(
037: "yyyy'-'MM'-'dd'T'HH':'mm':'ss");
038:
039: static {
040: pseudoISO8601DateFormat
041: .setTimeZone(TimeZone.getTimeZone("GMT"));
042: }
043:
044: /** the Metadata object this schema instance operates on */
045: protected Metadata meta;
046: private XMPSchema schema;
047:
048: /**
049: * Main constructor.
050: * @param meta the Metadata object to wrao
051: * @param schema the XMP schema for which this adapter was written
052: */
053: public XMPSchemaAdapter(Metadata meta, XMPSchema schema) {
054: if (meta == null) {
055: throw new NullPointerException(
056: "Parameter meta must not be null");
057: }
058: if (schema == null) {
059: throw new NullPointerException(
060: "Parameter schema must not be null");
061: }
062: this .meta = meta;
063: this .schema = schema;
064: }
065:
066: /** @return the XMP schema associated with this adapter */
067: public XMPSchema getSchema() {
068: return this .schema;
069: }
070:
071: /**
072: * Returns the QName for a given property
073: * @param propName the property name
074: * @return the resulting QName
075: */
076: protected QName getQName(String propName) {
077: return new QName(getSchema().getNamespace(), propName);
078: }
079:
080: /**
081: * Adds a String value to an array.
082: * @param propName the property name
083: * @param value the String value
084: * @param arrayType the type of array to operate on
085: */
086: private void addStringToArray(String propName, String value,
087: XMPArrayType arrayType) {
088: QName name = getQName(propName);
089: XMPProperty prop = meta.getProperty(name);
090: XMPArray array;
091: if (prop == null) {
092: array = new XMPArray(arrayType);
093: array.add(value);
094: prop = new XMPProperty(name, array);
095: meta.setProperty(prop);
096: } else {
097: prop.convertSimpleValueToArray(arrayType);
098: prop.getArrayValue().add(value);
099: }
100: }
101:
102: /**
103: * Adds a String value to an ordered array.
104: * @param propName the property name
105: * @param value the String value
106: */
107: protected void addStringToSeq(String propName, String value) {
108: addStringToArray(propName, value, XMPArrayType.SEQ);
109: }
110:
111: /**
112: * Adds a String value to an unordered array.
113: * @param propName the property name
114: * @param value the String value
115: */
116: protected void addStringToBag(String propName, String value) {
117: addStringToArray(propName, value, XMPArrayType.BAG);
118: }
119:
120: /**
121: * Formats a Date using ISO 8601 format in the default time zone.
122: * @param dt the date
123: * @return the formatted date
124: */
125: public static String formatISO8601Date(Date dt) {
126: //ISO 8601 cannot be expressed directly using SimpleDateFormat
127: Calendar cal = Calendar.getInstance();
128: cal.setTime(dt);
129: int offset = cal.get(Calendar.ZONE_OFFSET);
130: offset += cal.get(Calendar.DST_OFFSET);
131:
132: //DateFormat is operating on GMT so adjust for time zone offset
133: Date dt1 = new Date(dt.getTime() + offset);
134: StringBuffer sb = new StringBuffer(pseudoISO8601DateFormat
135: .format(dt1));
136:
137: offset /= (1000 * 60); //Convert to minutes
138:
139: if (offset == 0) {
140: sb.append('Z');
141: } else {
142: int zoneOffsetHours = offset / 60;
143: int zoneOffsetMinutes = Math.abs(offset % 60);
144: if (zoneOffsetHours > 0) {
145: sb.append('+');
146: } else {
147: sb.append('-');
148: }
149: if (zoneOffsetHours < 10) {
150: sb.append('0');
151: }
152: sb.append(zoneOffsetHours);
153: sb.append(':');
154: if (zoneOffsetMinutes < 10) {
155: sb.append('0');
156: }
157: sb.append(zoneOffsetMinutes);
158: }
159:
160: return sb.toString();
161: }
162:
163: /**
164: * Parses an ISO 8601 date and time value.
165: * @param dt the date and time value as an ISO 8601 string
166: * @return the parsed date/time
167: * @todo Parse formats other than yyyy-mm-ddThh:mm:ssZ
168: */
169: public static Date parseISO8601Date(final String dt) {
170: int offset = 0;
171: String parsablePart;
172: if (dt.endsWith("Z")) {
173: parsablePart = dt.substring(0, dt.length() - 1);
174: } else {
175: int pos;
176: int neg = 1;
177: pos = dt.lastIndexOf('+');
178: if (pos < 0) {
179: pos = dt.lastIndexOf('-');
180: neg = -1;
181: }
182: if (pos >= 0) {
183: String timeZonePart = dt.substring(pos);
184: parsablePart = dt.substring(0, pos);
185: offset = Integer.parseInt(timeZonePart.substring(1, 3)) * 60;
186: offset += Integer
187: .parseInt(timeZonePart.substring(4, 6));
188: offset *= neg;
189: } else {
190: parsablePart = dt;
191: }
192: }
193: Date d;
194: try {
195: d = pseudoISO8601DateFormat.parse(parsablePart);
196: } catch (ParseException e) {
197: throw new IllegalArgumentException(
198: "Invalid ISO 8601 date format: " + dt);
199: }
200: d.setTime(d.getTime() - offset * 60 * 1000);
201: return d;
202: }
203:
204: /**
205: * Adds a date value to an ordered array.
206: * @param propName the property name
207: * @param value the date value
208: */
209: protected void addDateToSeq(String propName, Date value) {
210: String dt = formatISO8601Date(value);
211: addStringToSeq(propName, dt);
212: }
213:
214: /**
215: * Set a date value.
216: * @param propName the property name
217: * @param value the date value
218: */
219: protected void setDateValue(String propName, Date value) {
220: String dt = formatISO8601Date(value);
221: setValue(propName, dt);
222: }
223:
224: /**
225: * Returns a date value.
226: * @param propName the property name
227: * @return the date value or null if the value is not set
228: */
229: protected Date getDateValue(String propName) {
230: String dt = getValue(propName);
231: if (dt == null) {
232: return null;
233: } else {
234: return parseISO8601Date(dt);
235: }
236: }
237:
238: /**
239: * Sets a language-dependent value.
240: * @param propName the property name
241: * @param lang the language ("x-default" or null for the default language)
242: * @param value the value
243: */
244: protected void setLangAlt(String propName, String lang, String value) {
245: if (lang == null) {
246: lang = XMPConstants.DEFAULT_LANGUAGE;
247: }
248: QName name = getQName(propName);
249: XMPProperty prop = meta.getProperty(name);
250: XMPArray array;
251: if (prop == null) {
252: array = new XMPArray(XMPArrayType.ALT);
253: array.add(value, lang);
254: prop = new XMPProperty(name, array);
255: meta.setProperty(prop);
256: } else {
257: prop.convertSimpleValueToArray(XMPArrayType.ALT);
258: removeLangAlt(lang, propName);
259: prop.getArrayValue().add(value, lang);
260: }
261: }
262:
263: /**
264: * Sets a simple value.
265: * @param propName the property name
266: * @param value the value
267: */
268: protected void setValue(String propName, String value) {
269: QName name = getQName(propName);
270: XMPProperty prop = meta.getProperty(name);
271: if (prop == null) {
272: prop = new XMPProperty(name, value);
273: meta.setProperty(prop);
274: } else {
275: prop.setValue(value);
276: }
277: }
278:
279: /**
280: * Returns a simple value.
281: * @param propName the property name
282: * @return the requested value or null if it isn't set
283: */
284: protected String getValue(String propName) {
285: QName name = getQName(propName);
286: XMPProperty prop = meta.getProperty(name);
287: if (prop == null) {
288: return null;
289: } else {
290: return prop.getValue().toString();
291: }
292: }
293:
294: /**
295: * Removes a language-dependent value from an alternative array.
296: * @param lang the language ("x-default" for the default language)
297: * @param propName the property name
298: */
299: protected void removeLangAlt(String lang, String propName) {
300: XMPProperty prop = meta.getProperty(getQName(propName));
301: XMPArray array;
302: if (prop != null && lang != null) {
303: array = prop.getArrayValue();
304: if (array != null) {
305: array.removeLangValue(lang);
306: } else {
307: if (lang.equals(prop.getXMLLang())) {
308: prop.setValue(null);
309: prop.setXMLLang(null);
310: }
311: }
312: }
313: }
314:
315: /**
316: * Returns a language-dependent value. If the value in the requested language is not available
317: * the value for the default language is returned.
318: * @param lang the language ("x-default" for the default language)
319: * @param propName the property name
320: * @return the requested value
321: */
322: protected String getLangAlt(String lang, String propName) {
323: XMPProperty prop = meta.getProperty(getQName(propName));
324: XMPArray array;
325: if (prop == null) {
326: return null;
327: } else {
328: array = prop.getArrayValue();
329: if (array != null) {
330: return array.getLangValue(lang);
331: } else {
332: return prop.getValue().toString();
333: }
334: }
335: }
336:
337: /**
338: * Returns an object array representation of the property's values.
339: * @param propName the property name
340: * @return the object array or null if the property isn't set
341: */
342: protected Object[] getObjectArray(String propName) {
343: XMPProperty prop = meta.getProperty(getQName(propName));
344: if (prop == null) {
345: return null;
346: }
347: XMPArray array = prop.getArrayValue();
348: if (array != null) {
349: return array.toObjectArray();
350: } else {
351: return new Object[] { prop.getValue() };
352: }
353: }
354:
355: /**
356: * Returns a String array representation of the property's values. Complex values to converted
357: * to Strings using the toString() method.
358: * @param propName the property name
359: * @return the String array or null if the property isn't set
360: */
361: protected String[] getStringArray(String propName) {
362: Object[] arr = getObjectArray(propName);
363: if (arr == null) {
364: return null;
365: }
366: String[] res = new String[arr.length];
367: for (int i = 0, c = res.length; i < c; i++) {
368: res[i] = arr[i].toString();
369: }
370: return res;
371: }
372:
373: }
|