001 /*
002 * Copyright 2007-2008 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.krad.util.documentserializer;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.kuali.rice.krad.util.KRADConstants;
020
021 import java.util.StringTokenizer;
022
023 /**
024 * This is a implementation of a trie/prefix tree of that contains metadata about property serializability
025 * during the document serialization process.
026 *
027 */
028 public class PropertySerializerTrie {
029 private static final String PROPERTY_NAME_COMPONENT_SEPARATOR = ".";
030 private PropertySerializerTrieNode rootNode;
031
032 public PropertySerializerTrie() {
033 rootNode = new PropertySerializerTrieNode(KRADConstants.EMPTY_STRING, KRADConstants.EMPTY_STRING);
034 }
035
036 /**
037 * Registers a new serializable property so that all of its primitives are serialized. All nesting properties
038 * will be serialized only to render open/close tags to maintain consistency with the document structure, unless
039 * they are registered as well.
040 *
041 * For example, if only property "document.a.b" is registered, then the XML will look like the following:
042 *
043 * <document>
044 * <a>
045 * <b>
046 * <primitiveOfB>valueOfPrimitive</primitiveOfB>
047 * </b>
048 * </a>
049 * </document>
050 *
051 * That is, primitives of "document" and "document.a" will not be serialized unless those property strings are registered.
052 *
053 * @param propertyName
054 * @param setPropertySerializabilityToObjectAndAllPrimitivesForAll
055 */
056 public void addSerializablePropertyName(String propertyName, boolean setPropertySerializabilityToObjectAndAllPrimitivesForAll) {
057 if (propertyName == null) {
058 throw new IllegalArgumentException("Null attribute name specified");
059 }
060 if (StringUtils.isBlank(propertyName)) {
061 rootNode.setPropertySerializabilityToObjectAndAllPrimitives();
062 }
063 else {
064 StringTokenizer tok = new StringTokenizer(propertyName, PROPERTY_NAME_COMPONENT_SEPARATOR, false);
065 StringBuilder buf = new StringBuilder();
066
067 if(setPropertySerializabilityToObjectAndAllPrimitivesForAll)
068 rootNode.setPropertySerializabilityToObjectAndAllPrimitives();
069
070 PropertySerializerTrieNode currentNode = rootNode;
071 while (tok.hasMoreTokens()) {
072 String attributeNameComponent = tok.nextToken();
073 validateAttributeNameComponent(attributeNameComponent);
074
075 buf.append(attributeNameComponent);
076
077 // create a new node or retrieve existing node for this name component
078 PropertySerializerTrieNode childNode = currentNode.getChildNode(attributeNameComponent);
079 if (childNode == null) {
080 childNode = new PropertySerializerTrieNode(buf.toString(), attributeNameComponent);
081 currentNode.addChildNode(childNode);
082 }
083
084 if (tok.hasMoreTokens()) {
085 buf.append(PROPERTY_NAME_COMPONENT_SEPARATOR);
086 }
087 currentNode = childNode;
088 if(setPropertySerializabilityToObjectAndAllPrimitivesForAll)
089 currentNode.setPropertySerializabilityToObjectAndAllPrimitives();
090 }
091
092 currentNode.setPropertySerializabilityToObjectAndAllPrimitives();
093 }
094 }
095
096 /**
097 * Retrieves the metadata about the given property name
098 *
099 * @param propertyName
100 * @return
101 */
102 public PropertySerializabilityMetadata getPropertySerializabilityMetadata(String propertyName) {
103 if (propertyName == null) {
104 throw new IllegalArgumentException("Null attribute name specified");
105 }
106 if (StringUtils.isBlank(propertyName)) {
107 return rootNode;
108 }
109 else {
110 StringTokenizer tok = new StringTokenizer(propertyName, PROPERTY_NAME_COMPONENT_SEPARATOR, false);
111
112 PropertySerializerTrieNode currentNode = rootNode;
113 while (tok.hasMoreTokens()) {
114 String attributeNameComponent = tok.nextToken();
115 validateAttributeNameComponent(attributeNameComponent);
116
117 // retrieve the child node for this name component
118 PropertySerializerTrieNode childNode = currentNode.getChildNode(attributeNameComponent);
119 if (childNode == null) {
120 // we didn't find a child node, so we know that something wasn't added with the prefix we're processing
121 return null;
122 }
123 else {
124 // keep going until we hit the last token, at which case we'll get out of this loop
125 currentNode = childNode;
126 }
127 }
128 return currentNode;
129 }
130 }
131
132 /**
133 * Returns the root node of the trie
134 *
135 * @return
136 */
137 public PropertySerializabilityMetadata getRootPropertySerializibilityMetadata() {
138 return rootNode;
139 }
140
141 protected void validateAttributeNameComponent(String attributeNameComponent) {
142 if (StringUtils.isBlank(attributeNameComponent)) {
143 throw new IllegalArgumentException("Blank attribute name component specified");
144 }
145 }
146 }