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 }