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