// Copyright 2006 Google Inc. // // Licensed 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 com.google.enterprise.connector.spi; import com.google.enterprise.connector.spiimpl.ValueImpl; import com.google.enterprise.connector.util.InputStreamFactory; import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import java.util.logging.Logger; /** * Wrapper class for all data items from a repository. Connector implementors * create instances of this class by calling the appropriate static factory * method. The factory methods are named to reflect the type data they carry, * thus: * <ul> * <li> {@code getStringValue} creates an object carrying a string</li> * <li> {@code getBinaryValue} creates an object carrying binary data * (stream or byte array)</li> * <li> {@code getLongValue} creates an object carrying an integer</li> * <li> {@code getDoubleValue} creates an object carrying a * floating-point value</li> * <li> {@code getDateValue} creates an object carrying a date</li> * <li> {@code getBooleanValue} creates an object carrying a boolean</li> * </ul> * In addition, some of the factory methods are overloaded for the convenience * of the connector developer. The implementations attempt to convert from the * parameter type to the base type indicated in the factory method's name. * * @since 1.0 */ public abstract class Value { private static final Logger LOGGER = Logger.getLogger(Value.class.getName()); /** * Creates a value carrying a String. * * @param stringValue the {@code String} value * @return a {@link Value} instance carrying this value */ public static Value getStringValue(String stringValue) { return ValueImpl.getStringValue(stringValue); } /** * Creates a value carrying binary data. * * @param inputStreamValue an {@code InputStream} containing the data * @return a {@link Value} instance carrying this data */ public static Value getBinaryValue(InputStream inputStreamValue) { return ValueImpl.getBinaryValue(inputStreamValue); } /** * Creates a value carrying binary data. * * @param inputStreamFactory an {@code InputStreamFactory} * @return a {@link Value} instance carrying this data */ public static Value getBinaryValue(InputStreamFactory inputStreamFactory) { return ValueImpl.getBinaryValue(inputStreamFactory); } /** * Creates a value carrying binary data. * * @param byteArrayValue an {@code byte} array containing the data * @return a {@link Value} instance carrying this data */ public static Value getBinaryValue(byte[] byteArrayValue) { return ValueImpl.getBinaryValue(byteArrayValue); } /** * Creates a value carrying an integer. * * @param longValue a {@code long} containing the data * @return a {@link Value} instance carrying this data */ public static Value getLongValue(long longValue) { return ValueImpl.getLongValue(longValue); } /** * Creates a value carrying an integer. * * @param doubleValue a {@code double} containing the data * @return a {@link Value} instance carrying this data */ public static Value getDoubleValue(double doubleValue) { return ValueImpl.getDoubleValue(doubleValue); } /** * Creates a value carrying a date. * * @param calendarValue a {@code Calendar} object containing the data * @return a {@link Value} instance carrying this data */ public static Value getDateValue(Calendar calendarValue) { return ValueImpl.getDateValue(calendarValue); } /** * Creates a value carrying a boolean. * * @param booleanValue a {@code boolean} containing the data * @return a {@link Value} instance carrying this data */ public static Value getBooleanValue(boolean booleanValue) { return ValueImpl.getBooleanValue(booleanValue); } /** * Creates a value carrying a boolean. * * @param stringValue A {@code String} containing the data. The String * is converted as follows: * <ul> * <li> Any case variant of the strings "f" and "false" return * {@code false}.</li> * <li> All other strings (including {@code null} and the empty * string) return {@code true}.</li> * </ul> * @return a {@link Value} instance carrying this data */ public static Value getBooleanValue(String stringValue) { return ValueImpl.getBooleanValue(stringValue); } /** * Creates a value carrying a principal. * * @param name a String representing the name of a principal. */ public static Value getPrincipalValue(String name) { return ValueImpl.getPrincipalValue(new Principal(name)); } /** * Creates a value carrying a principal. * * @param principal a Principal */ public static Value getPrincipalValue(Principal principal) { return ValueImpl.getPrincipalValue(principal); } /** * Convenience function for access to a single named value from a * {@link Document}. * * @param document the {@link Document} from which to extract the * {@link Value} * @param propertyName the name of the {@link Property} * @return the first {@link Value} of that named property, if there is one - * {@code null} otherwise * @throws RepositoryException */ public static Value getSingleValue(Document document, String propertyName) throws RepositoryException { Property p = document.findProperty(propertyName); if (p == null) { return null; } return p.nextValue(); } /** * Convenience function for access to a single string value from a * {@link Document}. * * @param document the {@link Document} from which to extract the * {@link Value} * @param propertyName the name of the {@link Property} * @return the String {@link Value} of that named property, if there is one - * {@code null} otherwise * @throws RepositoryException */ public static String getSingleValueString(Document document, String propertyName) throws RepositoryException { Value v = getSingleValue(document, propertyName); if (v == null) { return null; } return v.toString(); } /** * Returns a string representation of the {@link Value}. Connector developers * may count on this for debugging. * * @return a string representation of the {@link Value} */ @Override public abstract String toString(); private static final Calendar CALENDAR = Calendar.getInstance(); private static final SimpleDateFormat ISO8601_DATE_FORMAT_MILLIS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); private static final SimpleDateFormat ISO8601_DATE_FORMAT_SECS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); private static final SimpleDateFormat ISO8601_DATE_FORMAT_MINS = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ"); private static final SimpleDateFormat ISO8601_DATE_FORMAT_DATE = new SimpleDateFormat("yyyy-MM-dd"); private static final SimpleDateFormat RFC822_DATE_FORMAT = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss Z", Locale.ENGLISH); static { ISO8601_DATE_FORMAT_MILLIS.setCalendar(CALENDAR); ISO8601_DATE_FORMAT_MILLIS.setLenient(true); ISO8601_DATE_FORMAT_SECS.setCalendar(CALENDAR); ISO8601_DATE_FORMAT_SECS.setLenient(true); ISO8601_DATE_FORMAT_MINS.setCalendar(CALENDAR); ISO8601_DATE_FORMAT_MINS.setLenient(true); ISO8601_DATE_FORMAT_DATE.setCalendar(CALENDAR); ISO8601_DATE_FORMAT_DATE.setLenient(true); RFC822_DATE_FORMAT.setCalendar(CALENDAR); RFC822_DATE_FORMAT.setLenient(true); } /** * Sets the time zone used to format date values for the feed to the * given time zone. * * @param id the time zone ID, or {@code null} or * {@code ""} (empty string) to specify the default time zone * @see TimeZone#getTimeZone * @see TimeZone#getDefault * @since 2.4.4 */ public static synchronized void setFeedTimeZone(String id) { TimeZone tz; if (id == null || id.length() == 0) { id = "default"; // For the log message. tz = TimeZone.getDefault(); } else { tz = TimeZone.getTimeZone(id); } LOGGER.config("Setting feed time zone to " + id + " = " + tz.getID()); CALENDAR.setTimeZone(tz); } /** * Gets the time zone ID for the unit tests. * * @since 2.4.4 */ static synchronized String getFeedTimeZone() { return CALENDAR.getTimeZone().getID(); } /** * Formats a calendar object for the Feeds Protocol, using the * <a href="http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/date_and_time_format.htm"> * ISO-8601</a> format for just the date portion. See * <a href="http://www.google.com/support/enterprise/static/gsa/docs/admin/72/gsa_doc_set/feedsguide/feedsguide.html"> * Feeds Protocol Developer's Guide</a> * * @param calendar a {@code Calendar} * @return a String in ISO-8601 date format */ public static synchronized String calendarToFeedXml(Calendar calendar) { Date date = calendar.getTime(); return ISO8601_DATE_FORMAT_DATE.format(date); } /** * Formats a calendar object according to the * <a href="http://www.w3.org/Protocols/rfc822/#z28">RFC 822</a> * specification. * * @param calendar a {@code Calendar} * @return a String in RFC 822 format */ public static synchronized String calendarToRfc822(Calendar calendar) { Date date = calendar.getTime(); // Fix UTC time zone marker. The SimpleDateFormat Z pattern letter // always produces an offset string, e.g., "-0800" or "+000". For // UTC, the use of "GMT" (RFC 822) or "Z" (ISO 8601) is preferred. return RFC822_DATE_FORMAT.format(date).replaceFirst("\\+0000$", "GMT"); } /** * Formats a calendar object according to the * <a href="http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/date_and_time_format.htm"> * ISO-8601</a> specification. * * @param calendar a {@code Calendar} * @return a String in ISO-8601 format */ public static synchronized String calendarToIso8601(Calendar calendar) { Date date = calendar.getTime(); String isoString; if (calendar.isSet(Calendar.MILLISECOND)) { isoString = ISO8601_DATE_FORMAT_MILLIS.format(date); } else if (calendar.isSet(Calendar.SECOND)) { isoString = ISO8601_DATE_FORMAT_SECS.format(date); } else if (calendar.isSet(Calendar.MINUTE)) { isoString = ISO8601_DATE_FORMAT_MINS.format(date); } else { isoString = ISO8601_DATE_FORMAT_DATE.format(date); } // Fix UTC time zone marker. return isoString.replaceFirst("\\+0000$", "Z"); } private static synchronized Date iso8601ToDate(String s) throws ParseException { Date date = null; try { date = ISO8601_DATE_FORMAT_MILLIS.parse(s); } catch (ParseException e1) { try { date = ISO8601_DATE_FORMAT_SECS.parse(s); } catch (ParseException e2) { try { date = ISO8601_DATE_FORMAT_MINS.parse(s); } catch (ParseException e3) { date = ISO8601_DATE_FORMAT_DATE.parse(s); } } } return date; } /** * Parses a String in * <a href="http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/date_and_time_format.htm"> * ISO-8601</a> format (GMT zone) and returns an equivalent * {@code java.util.Calendar} object. * * @param dateString the date string to parse * @return a Calendar object * @throws ParseException if the the String can not be parsed */ public static synchronized Calendar iso8601ToCalendar(String dateString) throws ParseException { // Fix UTC time zone marker. For parsing, the Z pattern letter // does not accept "Z" for UTC. Date date = iso8601ToDate(dateString.replaceFirst("Z$", "+0000")); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar; } }