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 }