/* Copyright (c) 2008 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.gdata.util; import com.google.gdata.util.common.base.Charsets; import com.google.gdata.client.CoreErrorDomain; import com.google.gdata.data.BaseEntry; import com.google.gdata.data.BaseFeed; import com.google.gdata.data.Entry; import com.google.gdata.data.ExtensionProfile; import com.google.gdata.data.Feed; import com.google.gdata.data.IEntry; import com.google.gdata.data.IFeed; import com.google.gdata.data.ParseSource; import com.google.gdata.model.Element; import com.google.gdata.model.Schema; import com.google.gdata.wireformats.ContentCreationException; import com.google.gdata.wireformats.ContentValidationException; import com.google.gdata.wireformats.WireFormat; import com.google.gdata.wireformats.WireFormatParser; import com.google.gdata.wireformats.input.InputProperties; import com.google.gdata.wireformats.input.InputPropertiesBuilder; import java.io.IOException; import java.io.InputStreamReader; /** * Helper class with static parse methods to parse entries and feeds based on * the old or new data model as appropriate. * * */ public class ParseUtil { /** * Reads an entry from a parse source. This will use dynamic typing to adapt * the response to the most specific subtype available. */ public static IEntry readEntry(ParseSource source) throws IOException, ParseException, ServiceException { return readEntry(source, null, null, null); } /** * Reads an entry from a parse source, returning an instance of the requested * class, and using the given extension profile if parsing into the old data * model. If the requested class is null the response will be parsed into * the most specific subtype possible, using dynamic typing. If the requested * class is not null an instance of the requested type or a subtype will be * returned. */ @SuppressWarnings("unchecked") public static <T extends IEntry> T readEntry(ParseSource source, Class<T> requestedClass, ExtensionProfile extProfile, Schema schema) throws IOException, ParseException, ServiceException { if (source == null) { throw new NullPointerException("Null source"); } Class<? extends IEntry> entryClass = requestedClass; Class<? extends IEntry> responseClass = requestedClass; // Determine the parse entry type, if it is null we parse into an old // data model entry class. if (entryClass == null) { entryClass = Entry.class; responseClass = BaseEntry.class; } boolean isAdapting = isAdapting(entryClass); // Create a new entry instance. IEntry entry; try { entry = entryClass.newInstance(); } catch (IllegalAccessException iae) { throw new ServiceException( CoreErrorDomain.ERR.cantCreateEntry, iae); } catch (InstantiationException ie) { throw new ServiceException( CoreErrorDomain.ERR.cantCreateEntry, ie); } if (entry instanceof Element) { entry = entryClass.cast(parseElement(source, (Element) entry, schema)); } else { BaseEntry<?> baseEntry = (BaseEntry<?>) entry; // Initialize the extension profile (if not provided) if (extProfile == null) { extProfile = getExtProfile(baseEntry, isAdapting); } parseEntry(source, baseEntry, extProfile); // Adapt if requested and the entry contained a kind tag if (isAdapting) { BaseEntry<?> adaptedEntry = baseEntry.getAdaptedEntry(); if (responseClass.isInstance(adaptedEntry)) { entry = adaptedEntry; } } } return (T) responseClass.cast(entry); } /** * Reads a feed from a parse source. This will use dynamic typing to adapt * the response to the most specific subtype available. */ public static IFeed readFeed(ParseSource source) throws IOException, ParseException, ServiceException { return readFeed(source, null, null, null); } /** * This method provides the base implementation of feed reading using either * static or dynamic typing. If feedClass is non-null, the method is * guaranteed to return an instance of this type, otherwise adaptation will * be used to determine the type. The source object may be either an * InputStream, Reader, or XmlParser. */ @SuppressWarnings("unchecked") public static <F extends IFeed> F readFeed(ParseSource source, Class <F> requestedClass, ExtensionProfile extProfile, Schema schema) throws IOException, ParseException, ServiceException { if (source == null) { throw new NullPointerException("Null source"); } Class<? extends IFeed> feedClass = requestedClass; Class<? extends IFeed> responseClass = requestedClass; // Determine the parse feed type if (feedClass == null) { feedClass = Feed.class; responseClass = BaseFeed.class; } boolean isAdapting = isAdapting(feedClass); // Create a new feed instance. IFeed feed; try { feed = feedClass.newInstance(); } catch (IllegalAccessException iae) { throw new ServiceException( CoreErrorDomain.ERR.cantCreateFeed, iae); } catch (InstantiationException ie) { throw new ServiceException( CoreErrorDomain.ERR.cantCreateFeed, ie); } // Parse the content if (feed instanceof Element) { feed = feedClass.cast(parseElement(source, (Element) feed, schema)); } else { BaseFeed<?, ?> baseFeed = (BaseFeed<?, ?>) feed; // Initialize the extension profile (if not provided) if (extProfile == null) { extProfile = getExtProfile(baseFeed, isAdapting); } parseFeed(source, baseFeed, extProfile); // Adapt if requested and the feed contained a kind tag if (isAdapting) { BaseFeed<?, ?> adaptedFeed = baseFeed.getAdaptedFeed(); if (responseClass.isInstance(adaptedFeed)) { feed = adaptedFeed; } } } return (F) responseClass.cast(feed); } private static Element parseElement(ParseSource source, Element element, Schema schema) throws ParseException, IOException { WireFormat format = WireFormat.XML; InputProperties inProps = new InputPropertiesBuilder() .setElementMetadata(schema.bind(element.getElementKey())) .build(); WireFormatParser parser; if (source.getReader() != null) { parser = format.createParser(inProps, source.getReader(), Charsets.UTF_8); } else if (source.getInputStream() != null) { InputStreamReader reader = new InputStreamReader(source.getInputStream()); parser = format.createParser(inProps, reader, Charsets.UTF_8); } else if (source.getEventSource() != null) { parser = format.createParser(inProps, source.getEventSource()); } else { throw new IllegalStateException("Unexpected source: " + source); } try { return parser.parse(element); } catch (ContentCreationException e) { throw new ParseException( CoreErrorDomain.ERR.cantCreateExtension, e); } catch (ContentValidationException e) { throw e.toParseException(); } } private static void parseEntry(ParseSource source, BaseEntry<?> entry, ExtensionProfile extProfile) throws ParseException, IOException { if (source.getReader() != null) { entry.parseAtom(extProfile, source.getReader()); } else if (source.getInputStream() != null) { entry.parseAtom(extProfile, source.getInputStream()); } else if (source.getEventSource() != null) { entry.parseAtom(extProfile, source.getEventSource()); } else { throw new IllegalStateException("Unexpected source: " + source); } } private static void parseFeed(ParseSource source, BaseFeed<?, ?> feed, ExtensionProfile extProfile) throws ParseException, IOException { if (source.getReader() != null) { feed.parseAtom(extProfile, source.getReader()); } else if (source.getInputStream() != null) { feed.parseAtom(extProfile, source.getInputStream()); } else if (source.getEventSource() != null) { feed.parseAtom(extProfile, source.getEventSource()); } else { throw new IllegalStateException("Unexpected source: " + source); } } private static boolean isAdapting(Class<?> clazz) { return clazz == Entry.class || clazz == com.google.gdata.model.atom.Entry.class || clazz == Feed.class || clazz == com.google.gdata.model.atom.Feed.class; } private static ExtensionProfile getExtProfile(BaseEntry<?> entry, boolean isAdapting) { ExtensionProfile extProfile = null; extProfile = new ExtensionProfile(); ((BaseEntry<?>) entry).declareExtensions(extProfile); if (isAdapting) { extProfile.setAutoExtending(true); } return extProfile; } private static ExtensionProfile getExtProfile(BaseFeed<?, ?> feed, boolean isAdapting) { ExtensionProfile extProfile = null; extProfile = new ExtensionProfile(); feed.declareExtensions(extProfile); if (isAdapting) { extProfile.setAutoExtending(true); } return extProfile; } }