1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.exolab.castor.builder;
15
16 import java.util.ArrayList;
17 import java.util.Enumeration;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.castor.xml.JavaNaming;
28 import org.exolab.castor.builder.binding.ExtendedBinding;
29 import org.exolab.castor.builder.binding.XMLBindingComponent;
30 import org.exolab.castor.builder.binding.XPathHelper;
31 import org.exolab.castor.builder.binding.xml.Exclude;
32 import org.exolab.castor.builder.conflict.strategy.ClassNameConflictResolver;
33 import org.exolab.castor.builder.conflict.strategy.XPATHClassNameConflictResolver;
34 import org.exolab.castor.xml.schema.Annotated;
35 import org.exolab.castor.xml.schema.ElementDecl;
36 import org.exolab.castor.xml.schema.Group;
37 import org.exolab.castor.xml.schema.ModelGroup;
38 import org.exolab.castor.xml.schema.Order;
39 import org.exolab.castor.xml.schema.XMLType;
40 import org.exolab.javasource.JClass;
41
42
43
44
45
46
47
48 public class JClassRegistry {
49
50
51
52
53 private static final Log LOG = LogFactory.getLog(JClassRegistry.class);
54
55
56
57
58 private Set<String> _globalElements = new HashSet<String>();
59
60
61
62
63
64 private Map<String, JClass> _xpathToJClass = new HashMap<String, JClass>();
65
66
67
68
69 private Map<String, List<String>> _localNames = new HashMap<String, List<String>>();
70
71
72
73
74
75 private Map<String, String> _typedLocalNames = new HashMap<String, String>();
76
77
78
79
80
81
82 private JavaNaming _javaNaming;
83
84
85
86
87
88 private ClassNameConflictResolver _classNameConflictResolver =
89 new XPATHClassNameConflictResolver();
90
91
92
93
94
95
96 public void prebindGlobalElement(final String xpath) {
97 _globalElements.add(xpath);
98 }
99
100
101
102
103
104
105
106
107 public JClassRegistry(final ClassNameConflictResolver resolver, final JavaNaming javaNaming) {
108 _classNameConflictResolver = resolver;
109 _javaNaming = javaNaming;
110 }
111
112
113
114
115
116
117
118
119 public void bind(final JClass jClass, final XMLBindingComponent component, final String mode) {
120
121 Annotated annotated = component.getAnnotated();
122
123
124 String xPath = XPathHelper.getSchemaLocation(annotated, true);
125 String localXPath = getLocalXPath(xPath);
126 String untypedXPath = xPath;
127
128
129
130
131
132 String localName = getLocalName(xPath);
133 String typedLocalName = localName;
134
135 if (annotated instanceof ElementDecl) {
136 ElementDecl element = (ElementDecl) annotated;
137 String typexPath = XPathHelper.getSchemaLocation(element.getType());
138 xPath += "[" + typexPath + "]";
139 typedLocalName += "[" + typexPath + "]";
140 } else if (annotated instanceof Group) {
141 Group group = (Group) annotated;
142 if (group.getOrder() == Order.choice && !_globalElements.contains("/" + localXPath)) {
143 xPath += "/#choice";
144 }
145 }
146
147 ExtendedBinding binding = component.getBinding();
148 if (binding != null) {
149
150 if (binding.existsExclusion(typedLocalName)) {
151 Exclude exclusion = binding.getExclusion(typedLocalName);
152 if (exclusion.getClassName() != null) {
153 LOG.info("Dealing with exclusion for local element " + xPath + " as per binding file.");
154 jClass.changeLocalName(exclusion.getClassName());
155 }
156 return;
157 }
158
159
160 if (binding.existsForce(localName)) {
161
162 List<String> localNamesList = _localNames.get(localName);
163 memorizeCollision(xPath, localName, localNamesList);
164
165 LOG.info(
166 "Changing class name for local element " + xPath + " as per binding file (force).");
167 checkAndChange(jClass, annotated, untypedXPath, typedLocalName);
168 return;
169 }
170 }
171
172
173 String jClassLocalName = jClass.getLocalName();
174 String expectedClassNameDerivedFromXPath = _javaNaming.toJavaClassName(localName);
175 if (!jClassLocalName.equals(expectedClassNameDerivedFromXPath)) {
176 if (component.createGroupItem()) {
177 xPath += "/#item";
178 }
179 _xpathToJClass.put(xPath, jClass);
180 return;
181 }
182
183 if (mode.equals("field")) {
184 if (annotated instanceof ModelGroup) {
185 ModelGroup group = (ModelGroup) annotated;
186 final boolean isReference = group.isReference();
187 if (isReference) {
188 return;
189 }
190 }
191
192 if (annotated instanceof ElementDecl) {
193 ElementDecl element = (ElementDecl) annotated;
194
195 final boolean isReference = element.isReference();
196 if (isReference) {
197 ElementDecl referredElement = element.getReference();
198
199
200
201
202
203
204 Enumeration<?> possibleSubstitutes = referredElement.getSubstitutionGroupMembers();
205 if (possibleSubstitutes.hasMoreElements()) {
206 XMLType referredType = referredElement.getType();
207 String xPathType = XPathHelper.getSchemaLocation(referredType);
208 JClass typeJClass = _xpathToJClass.get(xPathType);
209 if (typeJClass != null) {
210 jClass.changeLocalName(typeJClass.getLocalName());
211 } else {
212
213 XMLBindingComponent temp = component;
214 temp.setView(referredType);
215 jClass.changeLocalName(temp.getJavaClassName());
216 component.setView(annotated);
217 }
218
219
220
221
222
223
224 }
225 return;
226 }
227 }
228 }
229
230 final boolean alreadyProcessed = _xpathToJClass.containsKey(xPath);
231
232
233 if (alreadyProcessed) {
234 JClass jClassAlreadyProcessed = _xpathToJClass.get(xPath);
235 jClass.changeLocalName(jClassAlreadyProcessed.getLocalName());
236 return;
237 }
238
239
240 _xpathToJClass.put(xPath, jClass);
241
242 if (LOG.isDebugEnabled()) {
243 LOG.debug("Binding JClass[" + jClass.getName() + "] for XML schema structure " + xPath);
244 }
245
246
247 final boolean isGlobalElement = _globalElements.contains(untypedXPath);
248 if (isGlobalElement) {
249 return;
250 }
251
252
253 if (mode.equals("field") && annotated instanceof ElementDecl) {
254 ElementDecl element = (ElementDecl) annotated;
255 final boolean isReference = element.isReference();
256 if (isReference) {
257 ElementDecl referredElement = element.getReference();
258
259
260
261
262
263 Enumeration<?> possibleSubstitutes = referredElement.getSubstitutionGroupMembers();
264 if (possibleSubstitutes.hasMoreElements()) {
265 String typeXPath = XPathHelper.getSchemaLocation(referredElement);
266 JClass referredJClass = _xpathToJClass.get(typeXPath + "_class");
267 jClass.changeLocalName(referredJClass.getSuperClass().getLocalName());
268 }
269 return;
270 }
271 }
272
273
274 final boolean conflictExistsWithGlobalElement = _globalElements.contains("/" + localXPath);
275 if (conflictExistsWithGlobalElement) {
276 LOG.info("Resolving conflict for local element " + xPath + " against global element.");
277
278 checkAndChange(jClass, annotated, untypedXPath, typedLocalName);
279
280
281 List<String> localNamesList = _localNames.get(localName);
282 memorizeCollision(xPath, localName, localNamesList);
283 return;
284 }
285
286 List<String> localNamesList = _localNames.get(localName);
287 memorizeCollision(xPath, localName, localNamesList);
288 if (localNamesList == null) {
289 String typedJClassName = _typedLocalNames.get(typedLocalName);
290 if (typedJClassName == null) {
291 _typedLocalNames.put(typedLocalName, jClass.getName());
292 }
293 } else {
294 LOG.info("Resolving conflict for local element " + xPath
295 + " against another local element of the same name.");
296 checkAndChange(jClass, annotated, untypedXPath, typedLocalName);
297 }
298 }
299
300
301
302
303
304
305
306
307 private void memorizeCollision(final String xPath, final String localName,
308 final List<String> localNamesList) {
309
310 if (localNamesList == null) {
311
312 List<String> arrayList = new ArrayList<String>();
313 arrayList.add(xPath);
314 _localNames.put(localName, arrayList);
315
316 } else {
317
318 if (!localNamesList.contains(xPath)) {
319 localNamesList.add(xPath);
320 }
321 }
322 }
323
324
325
326
327
328
329
330
331
332 private void checkAndChange(final JClass jClass, final Annotated annotated,
333 final String untypedXPath, final String typedLocalName) {
334
335
336 String typedJClassName = _typedLocalNames.get(typedLocalName);
337 if (typedJClassName != null) {
338
339 String localClassName = typedJClassName.substring(typedJClassName.lastIndexOf(".") + 1);
340 jClass.changeLocalName(localClassName);
341 } else {
342
343 changeClassInfoAsResultOfConflict(jClass, untypedXPath, typedLocalName, annotated);
344
345
346 _typedLocalNames.put(typedLocalName, jClass.getName());
347 }
348 }
349
350
351
352
353
354
355
356 private String getLocalName(final String xPath) {
357 String localName = xPath.substring(xPath.lastIndexOf("/") + 1);
358 if (localName.startsWith(ExtendedBinding.COMPLEXTYPE_ID)
359 || localName.startsWith(ExtendedBinding.SIMPLETYPE_ID)
360 || localName.startsWith(ExtendedBinding.ENUMTYPE_ID)
361 || localName.startsWith(ExtendedBinding.GROUP_ID)) {
362 localName = localName.substring(localName.indexOf(":") + 1);
363 }
364 return localName;
365 }
366
367
368
369
370
371
372
373 private String getLocalXPath(final String xPath) {
374 return xPath.substring(xPath.lastIndexOf("/") + 1);
375 }
376
377
378
379
380
381
382
383
384
385
386 private void changeClassInfoAsResultOfConflict(final JClass jClass, final String xpath,
387 final String typedXPath, final Annotated annotated) {
388 _classNameConflictResolver.changeClassInfoAsResultOfConflict(jClass, xpath, typedXPath,
389 annotated);
390 }
391
392
393
394
395
396
397 public void setClassNameConflictResolver(final ClassNameConflictResolver conflictResolver) {
398 _classNameConflictResolver = conflictResolver;
399 }
400
401
402
403
404
405
406
407 public void printStatistics(final XMLBindingComponent binding) {
408 Iterator<String> keyIterator = _localNames.keySet().iterator();
409 LOG.info("*** Summary ***");
410 if (binding.getBinding() != null && binding.getBinding().getForces() != null
411 && !binding.getBinding().getForces().isEmpty()) {
412 Iterator<String> forceIterator = binding.getBinding().getForces().iterator();
413 LOG.info("The following 'forces' have been enabled:");
414 while (forceIterator.hasNext()) {
415 String forceValue = forceIterator.next();
416 LOG.info(forceValue);
417 }
418 }
419 if (keyIterator.hasNext()) {
420 LOG.info("Local name conflicts encountered for the following element definitions");
421 while (keyIterator.hasNext()) {
422 String localName = keyIterator.next();
423 List<String> collisions = _localNames.get(localName);
424 if (collisions.size() > 1 && !ofTheSameType(collisions)) {
425 LOG.info(localName + ", with the following (element) definitions being involved:");
426 for (Iterator<String> iter = collisions.iterator(); iter.hasNext();) {
427 String xPath = iter.next();
428 LOG.info(xPath);
429 }
430 }
431 }
432 }
433
434 keyIterator = _localNames.keySet().iterator();
435 if (keyIterator.hasNext()) {
436 StringBuilder xmlFragment = new StringBuilder(32);
437 xmlFragment.append("<forces>\n");
438 while (keyIterator.hasNext()) {
439 String localName = keyIterator.next();
440 List<String> collisions = _localNames.get(localName);
441 if (collisions.size() > 1 && !ofTheSameType(collisions)) {
442 xmlFragment.append(" <force>");
443 xmlFragment.append(localName);
444 xmlFragment.append("</force>\n");
445 }
446 }
447 xmlFragment.append("</forces>");
448
449 LOG.info(xmlFragment.toString());
450 }
451
452 }
453
454
455
456
457
458
459
460 private boolean ofTheSameType(final List<String> collisions) {
461 boolean allSame = true;
462 Iterator<String> iterator = collisions.iterator();
463 String typeString = null;
464 while (iterator.hasNext()) {
465 String xPath = iterator.next();
466 String newTypeString = xPath.substring(xPath.indexOf("[") + 1, xPath.indexOf("]"));
467 if (typeString != null) {
468 if (!typeString.equals(newTypeString)) {
469 allSame = false;
470 break;
471 }
472 } else {
473 typeString = newTypeString;
474 }
475 }
476 return allSame;
477 }
478
479 }