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.parse;
016    
017    import java.util.ArrayList;
018    import java.util.HashMap;
019    import java.util.Iterator;
020    import java.util.List;
021    import java.util.ListIterator;
022    import java.util.Map;
023    
024    import org.apache.commons.logging.Log;
025    import org.apache.commons.logging.LogFactory;
026    import org.apache.hivemind.Element;
027    import org.apache.hivemind.ErrorHandler;
028    import org.apache.hivemind.schema.AttributeModel;
029    import org.apache.hivemind.schema.ElementModel;
030    import org.apache.hivemind.schema.Rule;
031    import org.apache.hivemind.schema.SchemaProcessor;
032    import org.apache.hivemind.schema.rules.BaseRule;
033    import org.apache.hivemind.schema.rules.CreateObjectRule;
034    import org.apache.hivemind.schema.rules.InvokeParentRule;
035    import org.apache.hivemind.schema.rules.ReadAttributeRule;
036    
037    /**
038     * Descriptor for the <conversion> module descriptor element. This descriptor implements the
039     * {@link Rule}interface and is added as a standard rule to the containing {@link ElementModel}.
040     * When processed it delegates to a {@link CreateObjectRule}, a bunch of {@link ReadAttributeRule},
041     * and finally an {@link InvokeParentRule}.
042     * 
043     * @author Howard Lewis Ship
044     */
045    public class ConversionDescriptor extends BaseRule
046    {
047        private static final Log LOG = LogFactory.getLog(ConversionDescriptor.class);
048    
049        private ErrorHandler _errorHandler;
050    
051        private String _className;
052    
053        private String _parentMethodName = "addElement";
054    
055        private Map _attributeNameMappingMap = new HashMap();
056    
057        /** @since 1.1 */
058        private List _attributeMappings = new ArrayList();
059    
060        private List _rules;
061    
062        private ElementModel _elementModel;
063    
064        public ConversionDescriptor(ErrorHandler errorHandler, ElementModel elementModel)
065        {
066            _errorHandler = errorHandler;
067            _elementModel = elementModel;
068        }
069    
070        /**
071         * @since 1.1
072         */
073        public List getAttributeMappings()
074        {
075            return _attributeMappings;
076        }
077    
078        /**
079         * Adds a mapping for an attribute; these come from <map> elements nested within the
080         * <conversion> element. A check for duplicate attribute mappings (that is, duplicated
081         * attribute name), and an error is logged (and the duplicate ignored).
082         */
083        public void addAttributeMapping(AttributeMappingDescriptor descriptor)
084        {
085            String attributeName = descriptor.getAttributeName();
086    
087            AttributeMappingDescriptor existing = (AttributeMappingDescriptor) _attributeNameMappingMap
088                    .get(attributeName);
089    
090            if (existing != null)
091            {
092                _errorHandler.error(
093                        LOG,
094                        ParseMessages.dupeAttributeMapping(descriptor, existing),
095                        descriptor.getLocation(),
096                        null);
097    
098                return;
099            }
100    
101            _attributeNameMappingMap.put(attributeName, descriptor);
102    
103            _attributeMappings.add(descriptor);
104        }
105    
106        /**
107         * @since 1.1
108         */
109        public String getClassName()
110        {
111            return _className;
112        }
113    
114        public void setClassName(String string)
115        {
116            _className = string;
117        }
118    
119        /**
120         * @since 1.1
121         */
122        public String getParentMethodName()
123        {
124            return _parentMethodName;
125        }
126    
127        public void setParentMethodName(String string)
128        {
129            _parentMethodName = string;
130        }
131    
132        /**
133         * @since 1.1
134         */
135        public void begin(SchemaProcessor processor, Element element)
136        {
137            for (Iterator i = _rules.iterator(); i.hasNext();)
138            {
139                Rule rule = (Rule) i.next();
140    
141                rule.begin(processor, element);
142            }
143        }
144    
145        /**
146         * @since 1.1
147         */
148        public void end(SchemaProcessor processor, Element element)
149        {
150            for (ListIterator i = _rules.listIterator(_rules.size()); i.hasPrevious();)
151            {
152                Rule rule = (Rule) i.previous();
153    
154                rule.end(processor, element);
155            }
156        }
157    
158        public void addRulesForModel()
159        {
160            _rules = new ArrayList();
161    
162            _rules.add(new CreateObjectRule(_className));
163    
164            addAttributeRules();
165    
166            _rules.add(new InvokeParentRule(_parentMethodName));
167        }
168    
169        private void addAttributeRules()
170        {
171            Iterator i = _elementModel.getAttributeModels().iterator();
172    
173            while (i.hasNext())
174            {
175                AttributeModel am = (AttributeModel) i.next();
176                String attributeName = am.getName();
177    
178                AttributeMappingDescriptor amd = (AttributeMappingDescriptor) _attributeNameMappingMap
179                        .get(attributeName);
180    
181                if (amd == null)
182                {
183                    _rules.add(new ReadAttributeRule(attributeName,
184                            constructPropertyName(attributeName), null, getLocation()));
185                }
186                else
187                {
188                    String propertyName = amd.getPropertyName();
189                    if (propertyName == null)
190                        propertyName = constructPropertyName(attributeName);
191    
192                    _rules.add(new ReadAttributeRule(attributeName, propertyName, null, amd
193                            .getLocation()));
194    
195                    _attributeNameMappingMap.remove(attributeName);
196                }
197            }
198    
199            if (!_attributeNameMappingMap.isEmpty())
200                _errorHandler.error(LOG, ParseMessages.extraMappings(
201                        _attributeNameMappingMap.keySet(),
202                        _elementModel), _elementModel.getLocation(), null);
203        }
204    
205        private String constructPropertyName(String attributeName)
206        {
207            int dashx = attributeName.indexOf('-');
208            if (dashx < 0)
209                return attributeName;
210    
211            int length = attributeName.length();
212            StringBuffer buffer = new StringBuffer(length);
213    
214            buffer.append(attributeName.substring(0, dashx));
215            boolean toUpper = true;
216    
217            for (int i = dashx + 1; i < length; i++)
218            {
219                char ch = attributeName.charAt(i);
220    
221                if (ch == '-')
222                {
223                    toUpper = true;
224                    continue;
225                }
226    
227                if (toUpper)
228                    ch = Character.toUpperCase(ch);
229    
230                buffer.append(ch);
231    
232                toUpper = false;
233            }
234    
235            return buffer.toString();
236        }
237    }