/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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 org.apache.shindig.social.core.util.xstream; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.inject.Inject; import com.google.inject.Injector; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.extended.ISO8601DateConverter; import com.thoughtworks.xstream.converters.extended.ISO8601GregorianCalendarConverter; import com.thoughtworks.xstream.converters.extended.ISO8601SqlTimestampConverter; import com.thoughtworks.xstream.converters.reflection.ReflectionProvider; import com.thoughtworks.xstream.io.HierarchicalStreamDriver; import com.thoughtworks.xstream.mapper.AttributeMapper; import com.thoughtworks.xstream.mapper.Mapper; import org.apache.shindig.social.core.model.EnumImpl; import org.apache.shindig.social.core.util.atom.AtomAttribute; import org.apache.shindig.social.core.util.atom.AtomAttributeConverter; import org.apache.shindig.social.core.util.atom.AtomContent; import org.apache.shindig.social.core.util.atom.AtomEntry; import org.apache.shindig.social.core.util.atom.AtomFeed; import org.apache.shindig.social.core.util.atom.AtomKeyValue; import org.apache.shindig.social.core.util.atom.AtomLinkConverter; import org.apache.shindig.social.opensocial.model.Account; import org.apache.shindig.social.opensocial.model.Activity; import org.apache.shindig.social.opensocial.model.Address; import org.apache.shindig.social.opensocial.model.BodyType; import org.apache.shindig.social.opensocial.model.ListField; import org.apache.shindig.social.opensocial.model.MediaItem; import org.apache.shindig.social.opensocial.model.Message; import org.apache.shindig.social.opensocial.model.Name; import org.apache.shindig.social.opensocial.model.Organization; import org.apache.shindig.social.opensocial.model.Person; import org.apache.shindig.social.opensocial.model.Url; import org.apache.shindig.social.opensocial.spi.DataCollection; import org.apache.shindig.social.opensocial.spi.RestfulCollection; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * */ public class XStream081Configuration implements XStreamConfiguration { /** * Defines the type of the list container when at the top level where there * are no methods to specify the name of the list. */ private static final Map<ConverterSet, List<ClassFieldMapping>> listElementMappingList = Maps.newHashMap(); /** * Specifies a priority sorted list of Class to Element Name mappings. */ private static final Map<ConverterSet, List<ClassFieldMapping>> elementMappingList = Maps.newHashMap(); /** * A list of omits, the potential field is the key, and if the class which the * field is in, is also in the list, the field is supressed. */ private static final Map<ConverterSet, Map<String, Class<?>[]>> omitMap = Maps.newHashMap(); /** * Maps elements names to classes. */ private static final Map<ConverterSet, Map<String, Class<?>>> elementClassMap = Maps.newHashMap(); private static final Map<ConverterSet, List<ImplicitCollectionFieldMapping>> itemFieldMappings = Maps.newHashMap(); private static final Map<ConverterSet, List<InterfaceFieldAliasMapping>> fieldAliasMappingList = Maps.newHashMap(); private static final Map<String, NamespaceSet> namepaces = Maps.newHashMap(); private static final String ATOM_NS = "http://www.w3.org/2005/Atom"; private static final String OS_NS = "http://ns.opensocial.org/2008/opensocial"; private static final String OSEARCH_NS = "http://a9.com/-/spec/opensearch/1.1"; static { // configure the name space mapping. This does not need to be all the elments in the // namespace, just the point of translation from one namespace to another. // It would have been good to use a standard parser/serializer approach, but // the xstream namespace implementation does not work exactly how we need it to. NamespaceSet atom = new NamespaceSet(); atom.addNamespace("xmlns", ATOM_NS); atom.addNamespace("xmlns:osearch",OSEARCH_NS); atom.addPrefixedElement("totalResults","osearch:totalResults"); atom.addPrefixedElement("startIndex","osearch:startIndex"); atom.addPrefixedElement("itemsPerPage","osearch:itemsPerPage"); namepaces.put("feed", atom); NamespaceSet os = new NamespaceSet(); atom.addNamespace("xmlns", OS_NS); namepaces.put("person", os); namepaces.put("activity", os); namepaces.put("account", os); namepaces.put("address", os); namepaces.put("bodyType", os); namepaces.put("message", os); namepaces.put("mediaItem", os); namepaces.put("name", os); namepaces.put("url", os); namepaces.put("reponse", os); namepaces.put("appdata", os); List<ClassFieldMapping> defaultElementMappingList = Lists.newArrayList(); // this is order specific, so put the more specified interfaces at the top. defaultElementMappingList.add(new ClassFieldMapping("feed", AtomFeed.class)); defaultElementMappingList.add(new ClassFieldMapping("content", AtomContent.class)); defaultElementMappingList.add(new ClassFieldMapping("activity", Activity.class)); defaultElementMappingList.add(new ClassFieldMapping("account", Account.class)); defaultElementMappingList.add(new ClassFieldMapping("address", Address.class)); defaultElementMappingList.add(new ClassFieldMapping("bodyType", BodyType.class)); defaultElementMappingList.add(new ClassFieldMapping("message", Message.class)); defaultElementMappingList.add(new ClassFieldMapping("mediaItem", MediaItem.class)); defaultElementMappingList.add(new ClassFieldMapping("name", Name.class)); defaultElementMappingList.add(new ClassFieldMapping("organization", Organization.class)); defaultElementMappingList .add(new ClassFieldMapping("person", Person.class)); defaultElementMappingList.add(new ClassFieldMapping("url", Url.class)); // this is an example of a class field mapping with context. If // ListField is mapped inside an element named emails, replace the element // name // that would have been defiend as fqcn ListField with email defaultElementMappingList.add(new ClassFieldMapping("ListField", ListField.class)); // some standard mappings not needed for runtime, but used in test, at the // bottom so as not // to conflict with other mappings. defaultElementMappingList.add(new ClassFieldMapping("response", RestfulCollection.class)); defaultElementMappingList.add(new ClassFieldMapping("appdata", DataCollection.class)); defaultElementMappingList.add(new ClassFieldMapping("list", List.class)); defaultElementMappingList.add(new ClassFieldMapping("map", Map.class)); elementMappingList.put(ConverterSet.DEFAULT, defaultElementMappingList); // element setup for RestfullCollection Responses List<ClassFieldMapping> collectionElementMappingList = Lists.newArrayList(); collectionElementMappingList.add(new ClassFieldMapping("feed", AtomFeed.class)); collectionElementMappingList.add(new ClassFieldMapping("content", AtomContent.class)); collectionElementMappingList.add(new ClassFieldMapping("activity", Activity.class)); collectionElementMappingList.add(new ClassFieldMapping("account", Account.class)); collectionElementMappingList.add(new ClassFieldMapping("address", Address.class)); collectionElementMappingList.add(new ClassFieldMapping("bodyType", BodyType.class)); collectionElementMappingList.add(new ClassFieldMapping("message", Message.class)); collectionElementMappingList.add(new ClassFieldMapping("mediaItem", MediaItem.class)); collectionElementMappingList.add(new ClassFieldMapping("name", Name.class)); collectionElementMappingList.add(new ClassFieldMapping("organization", Organization.class)); collectionElementMappingList.add(new ClassFieldMapping("person", Person.class)); collectionElementMappingList.add(new ClassFieldMapping("url", Url.class)); // this is an example of a class field mapping with context. If // ListField is mapped inside an element named emails, replace the element // name // that would have been defiend as fqcn ListField with email // collectionElementMappingList.add(new ClassFieldMapping("emails", "email", // ListField.class)); // collectionElementMappingList.add(new ClassFieldMapping("phoneNumbers", // "phone", ListField.class)); collectionElementMappingList.add(new ClassFieldMapping("ListField", ListField.class)); // some standard mappings not needed for runtime, but used in test, at the // bottom so as not // to conflict with other mappings. collectionElementMappingList.add(new ClassFieldMapping("response", RestfulCollection.class)); collectionElementMappingList.add(new ClassFieldMapping("list", List.class)); collectionElementMappingList.add(new ClassFieldMapping("map", Map.class)); elementMappingList.put(ConverterSet.COLLECTION, collectionElementMappingList); // ignore these fields in the specified classes. // Entries here might indicate a hole in the spec. // This not in the XSD for 81, but is in the wording of the spec. // omitMap.put("addresses", new Class[] { Person.class }); Map<String, Class<?>[]> defaultOmitMap = Maps.newHashMap(); defaultOmitMap.put("isOwner", new Class[] { Person.class }); defaultOmitMap.put("isViewer", new Class[] { Person.class }); omitMap.put(ConverterSet.DEFAULT, defaultOmitMap); Map<String, Class<?>> defaultElementClassMap = Maps.newHashMap(); defaultElementClassMap.put("feed", AtomFeed.class); defaultElementClassMap.put("content", AtomContent.class); defaultElementClassMap.put("person", Person.class); defaultElementClassMap.put("email", ListField.class); defaultElementClassMap.put("phone", ListField.class); defaultElementClassMap.put("list", ArrayList.class); defaultElementClassMap.put("map", ConcurrentHashMap.class); defaultElementClassMap.put("appdata", DataCollection.class); defaultElementClassMap.put("activity", Activity.class); defaultElementClassMap.put("account", Account.class); defaultElementClassMap.put("address", Address.class); defaultElementClassMap.put("bodyType", BodyType.class); defaultElementClassMap.put("message", Message.class); defaultElementClassMap.put("mediaItem", MediaItem.class); defaultElementClassMap.put("name", Name.class); defaultElementClassMap.put("organization", Organization.class); defaultElementClassMap.put("person", Person.class); defaultElementClassMap.put("url", Url.class); defaultElementClassMap.put("listField", ListField.class); elementClassMap.put(ConverterSet.DEFAULT, defaultElementClassMap); List<ImplicitCollectionFieldMapping> defaultItemFieldMappings = Lists.newArrayList(); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( AtomFeed.class, "entry", AtomEntry.class, "entry")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( AtomContent.class, "entry", AtomKeyValue.class, "entry")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "books", String.class, "books")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "cars", String.class, "cars")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "heroes", String.class, "heroes")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "food", String.class, "food")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "interests", String.class, "interests")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "languagesSpoken", String.class, "languagesSpoken")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "movies", String.class, "movies")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "music", String.class, "music")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "quotes", String.class, "quotes")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "sports", String.class, "sports")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "tags", String.class, "tags")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "turnOns", String.class, "turnOns")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "turnOffs", String.class, "turnOffs")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "tvShows", String.class, "tvShows")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "emails", ListField.class, "emails")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "phoneNumbers", ListField.class, "phoneNumbers")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "ims", ListField.class, "ims")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "photos", ListField.class, "photos")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "activities", Activity.class, "activities")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "addresses", Address.class, "addresses")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "organizations", Organization.class, "organizations")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "urls", Url.class, "urls")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Person.class, "lookingFor", EnumImpl.class, "lookingFor")); defaultItemFieldMappings.add(new ImplicitCollectionFieldMapping( Activity.class, "mediaItems", MediaItem.class, "mediaItems")); itemFieldMappings.put(ConverterSet.DEFAULT, defaultItemFieldMappings); List<ClassFieldMapping> defaultListElementMappingList = Lists.newArrayList(); listElementMappingList.put(ConverterSet.DEFAULT, defaultListElementMappingList); List<InterfaceFieldAliasMapping> defaultFieldAliasMappingList = Lists.newArrayList(); // defaultFieldAliasMappingList.add(new // InterfaceFieldAliasMapping("address",ListField.class,"value","urls")); // defaultFieldAliasMappingList.add(new // InterfaceFieldAliasMapping("address", // ListField.class,"value","profileSong")); // defaultFieldAliasMappingList.add(new // InterfaceFieldAliasMapping("address", // ListField.class,"value","profileVideo")); fieldAliasMappingList.put(ConverterSet.DEFAULT, defaultFieldAliasMappingList); } /** * The Guice injector, used for creating new instances of the model. */ private Injector injector; /** * */ @Inject public XStream081Configuration(Injector injector) { this.injector = injector; } private Map<String, Class<?>> getElementClassMap(ConverterSet c) { Map<String, Class<?>> ecm = elementClassMap.get(c); if (ecm == null) { ecm = elementClassMap.get(ConverterSet.DEFAULT); } return ecm; } private List<ClassFieldMapping> getElementMappingList(ConverterSet c) { List<ClassFieldMapping> eml = elementMappingList.get(c); if (eml == null) { eml = elementMappingList.get(ConverterSet.DEFAULT); } return eml; } private List<ClassFieldMapping> getListElementMappingList(ConverterSet c) { List<ClassFieldMapping> leml = listElementMappingList.get(c); if (leml == null) { leml = listElementMappingList.get(ConverterSet.DEFAULT); } return leml; } private Map<String, Class<?>[]> getOmitMap(ConverterSet c) { Map<String, Class<?>[]> om = omitMap.get(c); if (om == null) { om = omitMap.get(ConverterSet.DEFAULT); } return om; } private Converter[] getConverters(Mapper mapper, ConverterSet c) { return new Converter[] { new MapConverter(mapper), new RestfullCollectionConverter(mapper), new DataCollectionConverter(mapper), new AtomLinkConverter()}; } /** * @param c * @return */ private List<ImplicitCollectionFieldMapping> getItemFieldMappings( ConverterSet c) { List<ImplicitCollectionFieldMapping> om = itemFieldMappings.get(c); if (om == null) { om = itemFieldMappings.get(ConverterSet.DEFAULT); } return om; } /** * @param c * @return */ private List<InterfaceFieldAliasMapping> getFieldAliasMappingList( ConverterSet c) { List<InterfaceFieldAliasMapping> om = fieldAliasMappingList.get(c); if (om == null) { om = fieldAliasMappingList.get(ConverterSet.DEFAULT); } return om; } /** * {@inheritDoc} * * @param writerStack * * @see org.apache.shindig.social.core.util.xstream.XStreamConfiguration#getXStream(org.apache.shindig.social.core.util.xstream.XStreamConfiguration.ConverterSet, * com.thoughtworks.xstream.converters.reflection.ReflectionProvider, * com.thoughtworks.xstream.mapper.Mapper, * com.thoughtworks.xstream.io.HierarchicalStreamDriver) */ public ConverterConfig getConverterConfig(ConverterSet c, ReflectionProvider rp, Mapper dmapper, HierarchicalStreamDriver driver, WriterStack writerStack) { InterfaceFieldAliasingMapper emapper = new InterfaceFieldAliasingMapper( dmapper, writerStack, getFieldAliasMappingList(c)); InterfaceClassMapper fmapper = new InterfaceClassMapper(writerStack, emapper, getElementMappingList(c), getListElementMappingList(c), getItemFieldMappings(c), getOmitMap(c), getElementClassMap(c)); AttributeMapper amapper = new AttributeMapper(fmapper); XStream xstream = new XStream(rp, amapper, driver); amapper.addAttributeFor(AtomAttribute.class); for (Converter converter : getConverters(fmapper, c)) { xstream.registerConverter(converter); } xstream.registerConverter(new ISO8601DateConverter()); xstream.registerConverter(new ISO8601GregorianCalendarConverter()); xstream.registerConverter(new ISO8601SqlTimestampConverter()); xstream.registerConverter(new GuiceBeanConverter(fmapper, injector)); xstream.registerConverter(new AtomAttributeConverter()); xstream.setMode(XStream.NO_REFERENCES); return new ConverterConfig(fmapper,xstream); } /** * {@inheritDoc} * @see org.apache.shindig.social.core.util.xstream.XStreamConfiguration#getNameSpaces() */ public Map<String, NamespaceSet> getNameSpaces() { return namepaces; } }