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.impl;
016    
017    import java.util.ArrayList;
018    import java.util.HashMap;
019    import java.util.List;
020    import java.util.Map;
021    
022    import org.apache.hivemind.Element;
023    import org.apache.hivemind.ErrorLog;
024    import org.apache.hivemind.internal.Module;
025    import org.apache.hivemind.schema.ElementModel;
026    import org.apache.hivemind.schema.Schema;
027    import org.apache.hivemind.schema.SchemaProcessor;
028    import org.apache.hivemind.schema.Translator;
029    
030    /**
031     * Used to assemble all the {@link org.apache.hivemind.internal.Contribution}s contributed to an
032     * {@link org.apache.hivemind.internal.ConfigurationPoint} while converting the XML (represented as
033     * {@link org.apache.hivemind.Element}s into Java objects.
034     * 
035     * @author Howard Lewis Ship
036     */
037    public final class SchemaProcessorImpl implements SchemaProcessor
038    {
039        private ErrorLog _errorLog;
040    
041        private Schema _schema;
042    
043        /**
044         * The assembled elements that will be contributed into the ConfigurationPoint.
045         */
046        private List _elements = new ArrayList();
047    
048        private boolean _canElementsBeMapped;
049    
050        private Map _mappedElements = new HashMap();
051    
052        private List _stack = new ArrayList();
053    
054        private Module _contributingModule;
055    
056        /**
057         * Map on element name to {@link SchemaElement}.
058         */
059        private Map _elementMap = new HashMap();
060    
061        /**
062         * Used to track the nesting of elements.
063         */
064        private List _elementStack = new ArrayList();
065    
066        public SchemaProcessorImpl(ErrorLog errorLog, Schema schema)
067        {
068            _errorLog = errorLog;
069            _schema = schema;
070            _stack.add(this);
071    
072            if (_schema != null)
073            {
074                List l = _schema.getElementModel();
075    
076                int count = l.size();
077                for (int i = 0; i < count; i++)
078                {
079                    ElementModel model = (ElementModel) l.get(i);
080                    _elementMap.put(model.getElementName(), new SchemaElement(this, model));
081                }
082    
083                _canElementsBeMapped = schema.canInstancesBeKeyed();
084            }
085        }
086    
087        /**
088         * Invoked over reflection by the {@link org.apache.hivemind.schema.rules.InvokeParentRule}.
089         */
090        public void addElement(Object element)
091        {
092            _elements.add(element);
093    
094            if (_canElementsBeMapped)
095            {
096                Element currentElement = peekElement();
097                String keyAttribute = _activeElement.getKeyAttribute();
098    
099                String expandedKey = getContributingModule().expandSymbols(
100                        currentElement.getAttributeValue(keyAttribute),
101                        currentElement.getLocation());
102    
103                Translator t = getAttributeTranslator(keyAttribute);
104    
105                Object finalValue = t.translate(
106                        getContributingModule(),
107                        Object.class,
108                        expandedKey,
109                        currentElement.getLocation());
110    
111                _mappedElements.put(finalValue, element);
112            }
113        }
114    
115        public List getElements()
116        {
117            return _elements;
118        }
119    
120        public Map getMappedElements()
121        {
122            if (_canElementsBeMapped)
123                return _mappedElements;
124    
125            return null;
126        }
127    
128        public void push(Object object)
129        {
130            _stack.add(object);
131        }
132    
133        public Object pop()
134        {
135            if (_stack.isEmpty())
136                throw new ArrayIndexOutOfBoundsException(ImplMessages.schemaStackViolation(this));
137    
138            return _stack.remove(_stack.size() - 1);
139        }
140    
141        public Object peek()
142        {
143            return peek(0);
144        }
145    
146        public Object peek(int depth)
147        {
148            int count = _stack.size();
149    
150            int position = count - 1 - depth;
151    
152            if (position < 0)
153                throw new ArrayIndexOutOfBoundsException(ImplMessages.schemaStackViolation(this));
154    
155            return _stack.get(count - 1 - depth);
156        }
157    
158        public Module getContributingModule()
159        {
160            return _contributingModule;
161        }
162    
163        /** @since 1.1 */
164    
165        public Module getDefiningModule()
166        {
167            return _schema.getDefiningModule();
168        }
169    
170        public String getElementPath()
171        {
172            StringBuffer buffer = new StringBuffer();
173            int count = _elementStack.size();
174    
175            for (int i = 0; i < count; i++)
176            {
177                if (i > 0)
178                    buffer.append('/');
179    
180                buffer.append(((Element) _elementStack.get(i)).getElementName());
181            }
182    
183            return buffer.toString();
184        }
185    
186        private void pushElement(Element element)
187        {
188            _elementStack.add(element);
189        }
190    
191        private Element peekElement()
192        {
193            return (Element) _elementStack.get(_elementStack.size() - 1);
194        }
195    
196        private void popElement()
197        {
198            _elementStack.remove(_elementStack.size() - 1);
199        }
200    
201        /**
202         * Processes a single extension.
203         */
204        public void process(List elements, Module contributingModule)
205        {
206            if (elements == null)
207                return;
208    
209            if (_schema == null)
210            {
211                _elements.addAll(elements);
212                return;
213            }
214    
215            _contributingModule = contributingModule;
216    
217            int count = elements.size();
218    
219            for (int i = 0; i < count; i++)
220            {
221                Element e = (Element) elements.get(i);
222    
223                processRootElement(e);
224            }
225    
226            _contributingModule = null;
227        }
228    
229        private void processRootElement(Element element)
230        {
231            String name = element.getElementName();
232    
233            SchemaElement schemaElement = (SchemaElement) _elementMap.get(name);
234    
235            processElement(element, schemaElement);
236        }
237    
238        private SchemaElement _activeElement;
239    
240        private void processElement(Element element, SchemaElement schemaElement)
241        {
242            pushElement(element);
243    
244            if (schemaElement == null)
245                _errorLog
246                        .error(ImplMessages.unknownElement(this, element), element.getLocation(), null);
247            else
248            {
249                SchemaElement prior = _activeElement;
250    
251                schemaElement.validateAttributes(element);
252    
253                _activeElement = schemaElement;
254    
255                schemaElement.fireBegin(element);
256    
257                processNestedElements(element, schemaElement);
258    
259                schemaElement.fireEnd(element);
260    
261                _activeElement = prior;
262            }
263    
264            popElement();
265        }
266    
267        private void processNestedElements(Element element, SchemaElement schemaElement)
268        {
269            List l = element.getElements();
270            int count = l.size();
271    
272            for (int i = 0; i < count; i++)
273            {
274                Element nested = (Element) l.get(i);
275                String name = nested.getElementName();
276    
277                processElement(nested, schemaElement.getNestedElement(name));
278            }
279        }
280    
281        public Translator getContentTranslator()
282        {
283            return _activeElement.getContentTranslator();
284        }
285    
286        public Translator getAttributeTranslator(String attributeName)
287        {
288            return _activeElement.getAttributeTranslator(attributeName);
289        }
290    
291        public Translator getTranslator(String translator)
292        {
293            return getContributingModule().getTranslator(translator);
294        }
295    }