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.impl;
016
017import java.util.ArrayList;
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.hivemind.Element;
023import org.apache.hivemind.ErrorLog;
024import org.apache.hivemind.internal.Module;
025import org.apache.hivemind.schema.ElementModel;
026import org.apache.hivemind.schema.Schema;
027import org.apache.hivemind.schema.SchemaProcessor;
028import 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 */
037public 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}