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
015package org.apache.hivemind.lib.groovy;
016
017import java.util.HashMap;
018import java.util.Map;
019
020import groovy.xml.SAXBuilder;
021
022import org.xml.sax.Attributes;
023import org.xml.sax.ContentHandler;
024import org.xml.sax.Locator;
025import 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 */
041public 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}