01: /*
02: * Copyright (C) 2006, 2007 XStream Committers.
03: * All rights reserved.
04: *
05: * The software in this package is published under the terms of the BSD
06: * style license a copy of which has been included with this distribution in
07: * the LICENSE.txt file.
08: *
09: * Created on 15. March 2007 by Joerg Schaible
10: */
11: package com.thoughtworks.xstream.core;
12:
13: import com.thoughtworks.xstream.converters.ConversionException;
14: import com.thoughtworks.xstream.converters.Converter;
15: import com.thoughtworks.xstream.converters.ConverterLookup;
16: import com.thoughtworks.xstream.core.util.ObjectIdDictionary;
17: import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
18: import com.thoughtworks.xstream.io.path.Path;
19: import com.thoughtworks.xstream.io.path.PathTracker;
20: import com.thoughtworks.xstream.io.path.PathTrackingWriter;
21: import com.thoughtworks.xstream.mapper.Mapper;
22:
23: /**
24: * Abstract base class for a TreeMarshaller, that can build references.
25: *
26: * @author Joe Walnes
27: * @author Jörg Schaible
28: * @author Mauro Talevi
29: * @since 1.2
30: */
31: public abstract class AbstractReferenceMarshaller extends
32: TreeMarshaller {
33:
34: private ObjectIdDictionary references = new ObjectIdDictionary();
35: private ObjectIdDictionary implicitElements = new ObjectIdDictionary();
36: private PathTracker pathTracker = new PathTracker();
37: private Path lastPath;
38:
39: public AbstractReferenceMarshaller(HierarchicalStreamWriter writer,
40: ConverterLookup converterLookup, Mapper mapper) {
41: super (writer, converterLookup, mapper);
42: this .writer = new PathTrackingWriter(writer, pathTracker);
43: }
44:
45: public void convert(Object item, Converter converter) {
46: if (getMapper().isImmutableValueType(item.getClass())) {
47: // strings, ints, dates, etc... don't bother using references.
48: converter.marshal(item, writer, this );
49: } else {
50: Path currentPath = pathTracker.getPath();
51: Object existingReferenceKey = references.lookupId(item);
52: if (existingReferenceKey != null) {
53: writer.addAttribute(getMapper().aliasForAttribute(
54: "reference"), createReference(currentPath,
55: existingReferenceKey));
56: } else if (implicitElements.lookupId(item) != null) {
57: throw new ReferencedImplicitElementException(item,
58: currentPath);
59: } else {
60: Object newReferenceKey = createReferenceKey(currentPath);
61: if (lastPath == null
62: || !currentPath.isAncestor(lastPath)) {
63: fireValidReference(newReferenceKey);
64: lastPath = currentPath;
65: references.associateId(item, newReferenceKey);
66: } else {
67: implicitElements.associateId(item, newReferenceKey);
68: }
69: converter.marshal(item, writer, this );
70: }
71: }
72: }
73:
74: protected abstract String createReference(Path currentPath,
75: Object existingReferenceKey);
76:
77: protected abstract Object createReferenceKey(Path currentPath);
78:
79: protected abstract void fireValidReference(Object referenceKey);
80:
81: public static class ReferencedImplicitElementException extends
82: ConversionException {
83: /**
84: * @deprecated since 1.2.1
85: */
86: public ReferencedImplicitElementException(final String msg) {
87: super (msg);
88: }
89:
90: public ReferencedImplicitElementException(final Object item,
91: final Path path) {
92: super ("Cannot reference implicit element");
93: add("implicit-element", item.toString());
94: add("referencing-element", path.toString());
95: }
96: }
97: }
|