001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.hivemind.lib.groovy;
016    
017    import java.util.HashMap;
018    import java.util.Map;
019    
020    import groovy.xml.SAXBuilder;
021    
022    import org.xml.sax.Attributes;
023    import org.xml.sax.ContentHandler;
024    import org.xml.sax.Locator;
025    import org.xml.sax.helpers.AttributesImpl;
026    
027    /**
028     * The HiveMindBuilder is a <a href="http://groovy.codehaus.org/GroovyMarkup">groovy markup builder
029     * </a> which can be used to define HiveMind
030     * {@link org.apache.hivemind.parse.ModuleDescriptor module descriptors} using a Groovy script. A
031     * single Groovy script must only define one module descriptor.
032     * <p>
033     * The markup in the Groovy script is equivalent to the XML markup for module descriptors. The only
034     * difference being that any dashes in element names and attribute names (which would confuse the
035     * Groovy parser) are replaced by a camelCase notation. So for example
036     * <code>configuration-point</code> becomes <code>configurationPoint</code> in a Groovy script.
037     * 
038     * @since 1.1
039     * @author Knut Wannheden
040     */
041    public class HiveMindBuilder extends SAXBuilder
042    {
043        public static final Locator GROOVY_LOCATOR = new GroovyLocator();
044    
045        private static final Map CAMEL_TO_HYPHEN_MAP = new HashMap();
046    
047        public HiveMindBuilder(ContentHandler parser)
048        {
049            super(parser);
050    
051            parser.setDocumentLocator(GROOVY_LOCATOR);
052        }
053    
054        protected void nodeCompleted(Object parent, Object node)
055        {
056            super.nodeCompleted(parent, getHyphenatedName(node.toString()));
057        }
058    
059        protected void doStartElement(Object name, Attributes attributes)
060        {
061            super.doStartElement(
062                    getHyphenatedName(name.toString()),
063                    getHyphenatedAttributes(attributes));
064        }
065    
066        private String getHyphenatedName(String name)
067        {
068            String hyphenatedName = (String) CAMEL_TO_HYPHEN_MAP.get(name);
069    
070            if (hyphenatedName == null)
071            {
072                char[] chars = name.toCharArray();
073    
074                StringBuffer hyphenated = new StringBuffer();
075    
076                for (int i = 0; i < name.length(); i++)
077                {
078                    if (Character.isUpperCase(chars[i]))
079                        hyphenated.append('-').append(Character.toLowerCase(chars[i]));
080                    else
081                        hyphenated.append(chars[i]);
082                }
083    
084                hyphenatedName = hyphenated.toString();
085    
086                CAMEL_TO_HYPHEN_MAP.put(name, hyphenatedName);
087            }
088    
089            return hyphenatedName;
090        }
091    
092        private Attributes getHyphenatedAttributes(Attributes attributes)
093        {
094            AttributesImpl result = (AttributesImpl) attributes;
095    
096            for (int i = 0; i < result.getLength(); i++)
097            {
098                result.setLocalName(i, getHyphenatedName(result.getLocalName(i)));
099            }
100    
101            return result;
102        }
103    
104        private static class GroovyLocator implements Locator
105        {
106            public String getPublicId()
107            {
108                return null;
109            }
110    
111            public String getSystemId()
112            {
113                return null;
114            }
115    
116            public int getLineNumber()
117            {
118                try
119                {
120                    throw new Throwable();
121                }
122                catch (Throwable t)
123                {
124                    StackTraceElement[] trace = t.getStackTrace();
125    
126                    for (int i = 0; i < trace.length; i++)
127                    {
128                        String fileName = trace[i].getFileName();
129                                            if (fileName != null && fileName.endsWith(".groovy"))
130                            return trace[i].getLineNumber();
131                    }
132                }
133    
134                return -1;
135            }
136    
137            public int getColumnNumber()
138            {
139                return -1;
140            }
141        }
142    }