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