/* 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.data.introspection;
import com.google.gdata.util.common.xml.XmlNamespace;
import com.google.gdata.util.common.xml.XmlWriter;
import com.google.gdata.util.common.xml.XmlWriter.Attribute;
import com.google.gdata.client.CoreErrorDomain;
import com.google.gdata.client.Service;
import com.google.gdata.data.AttributeHelper;
import com.google.gdata.data.ExtensionPoint;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.Reference;
import com.google.gdata.data.TextConstruct;
import com.google.gdata.util.ContentType;
import com.google.gdata.util.Namespaces;
import com.google.gdata.util.ParseException;
import com.google.gdata.util.Version;
import com.google.gdata.util.XmlParser;
import org.xml.sax.Attributes;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* The Collection class defines the basic Java object model
* representation and XML parsing/generation support for an
* APP collection.
*
* The implementation is versioned to support the AtomPub draft version 9
* introspection format (used for the GData v1 implementation) as well
* as the final RFC5023 format (used for all other versions). The key
* difference between the two is that draft used an attribute for the
* collection title and a comma-delimited list for accepted MIME types,
* where the final version uses atom:title and repeating app:accept
* elements.
*
*
*/
public class Collection extends ExtensionPoint implements Reference,
ICollection {
private Version coreVersion = Service.getVersion();
private XmlNamespace atomPubNs = Namespaces.getAtomPubNs();
public Collection() {
}
public Collection(String href) {
this(href, null);
}
public Collection(String href, TextConstruct title) {
this.href = href;
this.title = title;
}
public Collection(String href, TextConstruct title, String ... accepts) {
this(href, title);
this.accepts = Arrays.asList(accepts);
}
/**
* Returns the accept type used in Atom service document to represent
* the fact that the service accepts Atom entry posting.
*/
public static String getAtomEntryAcceptType() {
// Earlier versions of the AtomPub spec (upon which GData v1 was
// based) used a hardcoded constant, later versions use the Atom
// entry MIME type.
if (Service.getVersion().isCompatible(Service.Versions.V1)) {
return "entry";
}
// We don't use the return value of ContentType.getAtomEntry because it
// contains charset encoding information that is misleading in this
// context, as we don't require utf-8 encoding of POSTed entries, we'll
// just always return them with this encoding.
return "application/atom+xml;type=entry";
}
/** The title of the collection */
private TextConstruct title;
public TextConstruct getTitle() { return title; }
public void setTitle(TextConstruct title) { this.title = title; }
/** The href of the collection */
private String href;
public String getHref() { return href; }
public void setHref(String href) { this.href = href; }
/** The mime type of the collection */
public String getType() {
return ContentType.getAtomFeed().toString();
}
/**
* The media types accepted by the collection. If null, then only
* Atom entries are supported.
*/
private List<String> accepts = new ArrayList<String>();
public List<String> getAcceptList() { return accepts; }
public void addAccept(String accept) {
accepts.add(accept);
}
private List<Categories> categoriesList = new ArrayList<Categories>();
public List<Categories> getCategoriesList() { return categoriesList; }
public void addCategories(Categories c) {
categoriesList.add(c);
}
/**
* Generates XML.
*
* @param w
* output writer
*
* @throws IOException
*/
@Override
public void generate(XmlWriter w, ExtensionProfile extProfile)
throws IOException {
ArrayList<XmlWriter.Attribute> attrs =
new ArrayList<XmlWriter.Attribute>(1);
if (coreVersion.isCompatible(Service.Versions.V1)) {
attrs.add(new Attribute("title", title.getPlainText()));
}
attrs.add(new XmlWriter.Attribute("href", href));
w.startElement(atomPubNs, "collection", attrs, null);
if (coreVersion.isCompatible(Service.Versions.V1)) {
if (accepts != null) {
StringBuffer acceptBuf = new StringBuffer();
for (String accept : accepts) {
if (acceptBuf.length() != 0) {
acceptBuf.append(',');
}
acceptBuf.append(accept);
}
w.simpleElement(atomPubNs, "accept", null, acceptBuf.toString());
}
} else {
if (title != null) {
title.generateAtom(w, "title");
}
for (String accept : accepts) {
if (accepts != null) {
w.simpleElement(atomPubNs, "accept", null, accept);
}
}
for (Categories categories : getCategoriesList()) {
categories.generate(w, extProfile);
}
}
generateExtensions(w, extProfile);
w.endElement(atomPubNs, "collection");
}
@Override
public void consumeAttributes(AttributeHelper attrHelper)
throws ParseException {
href = attrHelper.consume("href", true);
if (coreVersion.isCompatible(Service.Versions.V1)) {
String titleAttr = attrHelper.consume("title", true);
title = new PlainTextConstruct(titleAttr);
}
}
@Override
public XmlParser.ElementHandler getHandler(ExtensionProfile p,
String namespace, String localName, Attributes attrs) {
return new Handler(p, attrs);
}
/**
* XmlParser ElementHandler for {@code app:workspace}
*/
public class Handler extends ExtensionPoint.ExtensionHandler {
public Handler(ExtensionProfile extProfile, Attributes attrs) {
super(extProfile, Collection.class, attrs);
}
@Override
public XmlParser.ElementHandler getChildHandler(String namespace,
String localName,
Attributes attrs)
throws ParseException, IOException {
if (namespace.equals(Namespaces.atom)) {
if (localName.equals("title") &&
!coreVersion.isCompatible(Service.Versions.V1)) {
TextConstruct.ChildHandlerInfo chi =
TextConstruct.getChildHandler(attrs);
if (title != null) {
throw new ParseException(
CoreErrorDomain.ERR.duplicateTitle);
}
title = chi.textConstruct;
return chi.handler;
}
} else if (namespace.equals(atomPubNs.getUri())) {
if (localName.equals("accept")) {
return new AcceptHandler();
}
if (localName.equals("categories")) {
Categories newCategories = new Categories();
addCategories(newCategories);
return newCategories.new Handler(extProfile, attrs);
}
}
return super.getChildHandler(namespace, localName, attrs);
}
@Override
public void processEndElement() throws ParseException {
super.processEndElement();
if (title == null) {
throw new ParseException(
CoreErrorDomain.ERR.collectionTitleRequired);
}
}
/** app:accept element parser. */
class AcceptHandler extends XmlParser.ElementHandler {
@Override
public void processEndElement() {
if (value == null) {
value = "";
}
if (coreVersion.isCompatible(Service.Versions.V1)) {
accepts = Arrays.asList(value.split(","));
} else {
addAccept(value);
}
}
}
}
}