/* * Copyright (C) 2006, 2007, 2008 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. * * Created on 15. March 2007 by Joerg Schaible */ package com.thoughtworks.xstream.core; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.ConverterLookup; import com.thoughtworks.xstream.core.util.ObjectIdDictionary; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.path.Path; import com.thoughtworks.xstream.io.path.PathTracker; import com.thoughtworks.xstream.io.path.PathTrackingWriter; import com.thoughtworks.xstream.mapper.Mapper; /** * Abstract base class for a TreeMarshaller, that can build references. * * @author Joe Walnes * @author Jörg Schaible * @author Mauro Talevi * @since 1.2 */ public abstract class AbstractReferenceMarshaller extends TreeMarshaller { private ObjectIdDictionary references = new ObjectIdDictionary(); private ObjectIdDictionary implicitElements = new ObjectIdDictionary(); private PathTracker pathTracker = new PathTracker(); private Path lastPath; public AbstractReferenceMarshaller(HierarchicalStreamWriter writer, ConverterLookup converterLookup, Mapper mapper) { super(writer, converterLookup, mapper); this.writer = new PathTrackingWriter(writer, pathTracker); } public void convert(Object item, Converter converter) { if (getMapper().isImmutableValueType(item.getClass())) { // strings, ints, dates, etc... don't bother using references. converter.marshal(item, writer, this); } else { Path currentPath = pathTracker.getPath(); Object existingReferenceKey = references.lookupId(item); if (existingReferenceKey != null) { String attributeName = getMapper().aliasForSystemAttribute("reference"); if (attributeName != null) { writer.addAttribute(attributeName, createReference(currentPath, existingReferenceKey)); } } else if (implicitElements.lookupId(item) != null) { throw new ReferencedImplicitElementException(item, currentPath); } else { Object newReferenceKey = createReferenceKey(currentPath, item); if (lastPath == null || !currentPath.isAncestor(lastPath)) { fireValidReference(newReferenceKey); lastPath = currentPath; references.associateId(item, newReferenceKey); } else { implicitElements.associateId(item, newReferenceKey); } converter.marshal(item, writer, this); } } } protected abstract String createReference(Path currentPath, Object existingReferenceKey); protected abstract Object createReferenceKey(Path currentPath, Object item); protected abstract void fireValidReference(Object referenceKey); public static class ReferencedImplicitElementException extends ConversionException { /** * @deprecated since 1.2.1 */ public ReferencedImplicitElementException(final String msg) { super(msg); } public ReferencedImplicitElementException(final Object item, final Path path) { super("Cannot reference implicit element"); add("implicit-element", item.toString()); add("referencing-element", path.toString()); } } }