001 /*****************************************************************************
002 * Copyright (C) NanoContainer Organization. All rights reserved. *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file. *
007 * *
008 * Original code by James Strachan *
009 *****************************************************************************/
010
011 package org.nanocontainer.script.groovy.buildernodes;
012
013 import java.io.Serializable;
014 import java.util.Collections;
015 import java.util.HashSet;
016 import java.util.Set;
017
018 import org.nanocontainer.script.groovy.BuilderNode;
019 import java.util.Map;
020 import org.nanocontainer.script.NanoContainerMarkupException;
021 import java.util.Iterator;
022
023 /**
024 * Abstract base class for custom nodes. Also provides basic services and
025 * construction capabilities.
026 * @author James Strachan
027 * @author Paul Hammant
028 * @author Aslak Hellesøy
029 * @author Michael Rimov
030 * @author Mauro Talevi
031 * @version $Revision: 2443 $
032 */
033 abstract public class AbstractBuilderNode implements BuilderNode, Serializable {
034
035 /**
036 * The name of the node we're working with.
037 */
038 private final String nodeName;
039
040
041 /**
042 * A set of all possible supported attribute names.
043 */
044 private Set supportedAttributes = new HashSet();
045
046
047
048 /**
049 * Constructs a custom node builder. In derived classes you would
050 * typically create a default constructor and call addPossibleParent()/addAttribute()
051 * to customize the validation capabilities of the Node.
052 * @param nodeName the name of the node we're constructing.
053 */
054 public AbstractBuilderNode(final String nodeName) {
055 this.nodeName = nodeName;
056
057 }
058
059
060 /**
061 * Add an attribute to the list of ones supported by this node.
062 * @param name String the name of the attribute we support.
063 * @return AbstractBuilderNode (this) to allow for method chaining.
064 */
065 protected AbstractBuilderNode addAttribute(final String name) {
066 supportedAttributes.add(name);
067 return this;
068 }
069
070
071 public String getNodeName() {
072 return nodeName;
073 }
074
075
076 public Set getSupportedAttributes() {
077 return Collections.unmodifiableSet(supportedAttributes);
078 }
079
080 public String toString() {
081 return "Nanocontainer Builder Node: " + this.getClass().getName() + " (\"" + getNodeName() + "\")";
082 }
083
084 /**
085 * Checks that an attribute actually exists in the attirbute map. (The key
086 * exists and the value is non-null)
087 * @param attributes Map the current node's attributes.
088 * @param key String the attribute key we're looking for.
089 * @return boolean true if the attribute exists for the current node.
090 */
091 protected boolean isAttribute(final Map attributes, final String key) {
092 return attributes.containsKey(key) && attributes.get(key) != null;
093 }
094
095 /**
096 * {@inheritDoc}
097 * <p>This particular implementation checks all specified attribute keynames
098 * against the names supported in the node type. It does not type checking
099 * against the values passed in via the attributes.</p>
100 * @param specifiedAttributes the attributes as passed in by the groovy
101 * script.
102 * @throws NanoContainerMarkupException if an attribute is specified that
103 * is not recognized.
104 */
105 public void validateScriptedAttributes(final Map specifiedAttributes) throws NanoContainerMarkupException {
106 Set specifiedAttributeNames = specifiedAttributes.keySet();
107 if (this.getSupportedAttributes().containsAll(specifiedAttributeNames)) {
108 return;
109 }
110
111 Set unknownAttributes = new HashSet(specifiedAttributeNames);
112 unknownAttributes.removeAll(this.getSupportedAttributes());
113
114 StringBuffer errorMessage = new StringBuffer();
115 errorMessage.append("Found one or more unknown attributes for builder node '");
116 errorMessage.append(this.getNodeName());
117 errorMessage.append("': ");
118 errorMessage.append(convertSetToCommaDelimitedString(unknownAttributes));
119 errorMessage.append(". Recognized Attributes For this node are [");
120 errorMessage.append(convertSetToCommaDelimitedString(this.getSupportedAttributes()));
121 errorMessage.append("].");
122
123 throw new NanoContainerMarkupException(errorMessage.toString());
124 }
125
126 /**
127 * Utility function that takes a set and converts it to a comma delimited
128 * String with the format: key1, key2,.....
129 * @param specifiedSet Set the set to convert. For each object in the set,
130 * its toString() is called.
131 *
132 * @return String
133 */
134 private String convertSetToCommaDelimitedString(final Set specifiedSet) {
135
136 StringBuffer result = new StringBuffer();
137
138 boolean needComma = false;
139 for (Iterator i = specifiedSet.iterator(); i.hasNext();) {
140 if (needComma) {
141 result.append(",");
142 } else {
143 needComma = true;
144 }
145
146 result.append(i.next().toString());
147 }
148 return result.toString();
149 }
150
151 }