001 /** 002 * Copyright 2005-2012 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 }