001: /*
002: * SkeletValidator.java
003: *
004: * Created on November 25, 2004, 1:10 PM
005: */
006:
007: package com.finalist.jaggenerator;
008:
009: import com.finalist.jaggenerator.modules.*;
010:
011: import javax.swing.tree.TreeNode;
012: import javax.swing.tree.TreePath;
013: import java.util.*;
014:
015: /**
016: * @author Rudie Ekkelenkamp.
017: */
018: public class SkeletValidator {
019: private Root root = null;
020: private javax.swing.JTree tree = null;
021: private ConsoleLogger logger = null;
022: private HashMap entitiesByTableName = null;
023:
024: /**
025: * Creates a new instance of SkeletValidator
026: */
027: public SkeletValidator(Root root, javax.swing.JTree tree,
028: HashMap entitiesByTableName, ConsoleLogger logger) {
029: this .root = root;
030: this .tree = tree;
031: this .logger = logger;
032: this .entitiesByTableName = entitiesByTableName;
033: }
034:
035: /**
036: * Check on projectname, description, datasource name, jndi-name and mapping, etc..
037: *
038: * @return null if valid, returns a message string in case of an error.
039: */
040: public String validateSkelet() {
041: boolean atLeastOneCompositePK = false;
042: boolean valid = true;
043: boolean autoGeneratedStringInvalidHibernate = false;
044: boolean isRelatedEntityComposite = false;
045: String message = "";
046: String businessTier = (String) root.config
047: .getTemplateSettings().get(
048: JagGenerator.TEMPLATE_BUSINESS_TIER);
049: String webTier = (String) root.config.getTemplateSettings()
050: .get(JagGenerator.TEMPLATE_WEB_TIER);
051: String appServer = (String) root.config.getTemplateSettings()
052: .get(JagGenerator.TEMPLATE_APPLICATION_SERVER);
053: String useJava5 = (String) root.config.getTemplateSettings()
054: .get(JagGenerator.TEMPLATE_USE_JAVA5);
055:
056: // Do some basic checks before generation starts.
057: try {
058: // Make sure the projectname has been set!
059: if (root.app.nameText.getText() == null
060: || "".equals(root.app.nameText.getText())) {
061: valid = false;
062: message += "No valid application name has been set in the 'Application settings'.\r\n";
063: } else if (!root.app.nameText.getText().toLowerCase()
064: .equals(root.app.nameText.getText())) {
065: // Check if it is a valid name! Should be lowercase only!
066: valid = false;
067: message += "No valid application name in the 'Application settings'. Should be only lowercase!.\r\n";
068: }
069: } catch (Exception e) {
070: valid = false;
071: message += "No valid application name has been set in the 'Application settings'.\r\n";
072: }
073:
074: try {
075: // Make sure the applicationname has been set!
076: if (root.app.descriptionText.getText() == null
077: || "".equals(root.app.descriptionText.getText())) {
078: valid = false;
079: message += "No valid application description has been set in the 'Application settings'.\r\n";
080: } else {
081: // Check if the first character is uppercase!
082: String firstChar = root.app.descriptionText.getText()
083: .substring(0, 1);
084: if (!firstChar.toUpperCase().equals(firstChar)) {
085: valid = false;
086: message += "No valid application description has been set in the 'Application settings'. First character should be uppercase!\r\n";
087: }
088: }
089: } catch (Exception e) {
090: valid = false;
091: message += "No valid application description has been set in the 'Application settings'.\r\n";
092: }
093:
094: try {
095: // Make sure the rootpackage has been set!
096: if (root.app.rootPackageText.getText() == null
097: || "".equals(root.app.rootPackageText.getText())) {
098: valid = false;
099: message += "No valid root package set in the 'Application settings'.\r\n";
100: }
101: } catch (Exception e) {
102: valid = false;
103: message += "No valid root package set in the 'Application settings'.\r\n";
104: }
105:
106: try {
107: // Make sure the applicationname has been set!
108: if (root.datasource.jdbcURLCombo.getSelectedItem() == null
109: || "".equals(root.datasource.jdbcURLCombo
110: .getSelectedItem())) {
111: valid = false;
112: message += "No valid datasource URL set in the Datasource form.\r\n";
113: }
114: } catch (Exception e) {
115: valid = false;
116: message += "No valid datasource URL set in the Datasource form.\r\n";
117: }
118:
119: try {
120: // Make sure the datasource has been set!
121: if (root.datasource.jndiText.getText() == null
122: || "".equals(root.datasource.jndiText.getText())
123: || "jdbc/".equals(root.datasource.jndiText
124: .getText())) {
125: valid = false;
126: message += "No valid datasource jndi-name set in the Datasource form.\r\n";
127: }
128: } catch (Exception e) {
129: valid = false;
130: message += "No valid datasource jndi-name set in the Datasource form.\r\n";
131: }
132:
133: HashMap entityRefs = new HashMap();
134: ArrayList el = root.getEntityEjbs();
135: for (int i = 0; i < el.size(); i++) {
136: Entity e = (Entity) el.get(i);
137: // Create a hashmap with all current entity refs.
138: entityRefs.put(e.getRefName(), null);
139: }
140:
141: // Check if there have been entities marked as association entities without having 2 foreign keys.
142: for (int i = 0; i < el.size(); i++) {
143: Entity entity = (Entity) el.get(i);
144: if ("true".equals(entity.getIsAssociationEntity())) {
145: // Now check if it is a valid association entity.
146: if (entity.getRelations() != null
147: && entity.getRelations().size() == 2
148: && entity.getFields().size() == 2) {
149: // It's an entity with exaclty 2 foreign keys (targetMultiple is false).
150: if (((Relation) entity.getRelations().get(0))
151: .isTargetMultiple()
152: && ((Relation) entity.getRelations().get(1))
153: .isTargetMultiple()) {
154: // OK, it's a valid association.
155: } else {
156: // Both relation should be many-to-one relations.
157: valid = false;
158: message += "Entity "
159: + entity.getName()
160: + " is not a valid assocation Entity.\r\nThere should be exactly 2 many-to-one relations in the assocation entity.\r\n";
161: }
162: } else {
163: // Not valid. Exactly 2 forein keys are required.
164: valid = false;
165: message += "Entity "
166: + entity.getName()
167: + " is not a valid assocation Entity.\r\nThere should be exactly 2 many-to-one relations in the assocation entity.\r\n";
168: }
169: }
170:
171: }
172:
173: // Validate that the entity beans 'within' the session beans actually exist..
174: try {
175: ArrayList sl = root.getSessionEjbs();
176: for (int i = 0; i < sl.size(); i++) {
177: Session s = (Session) sl.get(i);
178: ArrayList allRefs = s.getEntityRefs();
179: for (int j = 0; j < allRefs.size(); j++) {
180: if (!entityRefs.containsKey(allRefs.get(j))) {
181: valid = false;
182: message += "The reference " + allRefs.get(j)
183: + " in Service Bean " + s.getRefName()
184: + " doesn't exist .\r\n";
185: }
186: }
187: // Make sure that all the entity refs are defined!
188: }
189: } catch (Exception e) {
190: valid = false;
191: message += "Error in Service Bean.\r\n";
192: }
193:
194: // Validate the Primary Keys on the Entity EJBs and composite primary keys.!
195: try {
196: // Make sure the applicationname has been set!
197: ArrayList nl = root.getEntityEjbs();
198: for (int i = 0; i < nl.size(); i++) {
199: Entity e = (Entity) nl.get(i);
200: List relations = e.getRelations();
201: for (int r = 0; r < relations.size(); r++) {
202: Relation relation = (Relation) relations.get(r);
203: if (relation.getRelatedEntity().isCompositeKey()) {
204: // The target entity of a relation shouldn't be a composite.
205: // JAG doesn't support this.
206: isRelatedEntityComposite = true;
207: }
208:
209: }
210: //Make sure that a display name maps one of its fields.
211: String displayName = "";
212: boolean validDisplayName = false;
213: if (e.getDisplayName() == null
214: || "".equals(e.getDisplayName().toString())) {
215: // Always valid if not set.
216: validDisplayName = true;
217: } else {
218: displayName = e.getDisplayName().toString();
219: }
220:
221: // Iterate over all entity fields. Make sure that autoGeneratedCheckBox has not been
222: // Marked for foreign keys.
223: List fields = e.getFields();
224: for (int j = 0; j < fields.size(); j++) {
225: Field field = (Field) fields.get(j);
226: if (displayName.equals(field.getName().toString())) {
227: validDisplayName = true;
228: }
229: if (field.autoGeneratedCheckBox.isSelected()
230: && field.foreignKeyCheckBox.isSelected()
231: && "false".equals(e
232: .getIsAssociationEntity())) {
233: logger
234: .log("Field: "
235: + field.getName()
236: + " of entity : "
237: + e.getName()
238: + " cannot have autogenerated primary key selected and be a foreign key!");
239: valid = false;
240: message += "Field: "
241: + field.getName()
242: + " of entity : "
243: + e.getName()
244: + " cannot have autogenerated primary key selected and be a foreign key!.\r\n";
245: }
246: if (field.autoGeneratedCheckBox.isSelected()) {
247: if ("java.lang.String".equalsIgnoreCase(field
248: .getType())) {
249: // In this case, check the size.
250: // For the hibernate uuid.hex generator, the size should be >= 32
251: String fieldSize = field.getSize();
252: try {
253: int size = Integer.parseInt(fieldSize);
254: if (size < 32) {
255: autoGeneratedStringInvalidHibernate = true;
256: }
257: } catch (Exception ex) {
258: // Size could not be determined.
259: // Assume that size was not set and the size will be large enough.
260: }
261: }
262: }
263:
264: }
265: if (!validDisplayName) {
266: // No valid name found for the display name.
267: logger.log("No valid display name set: "
268: + displayName);
269: valid = false;
270: message += "Display name " + displayName
271: + " of entity : " + e.getName()
272: + " should be a field of this entity.";
273: }
274: if (e.isCompositeCombo.getSelectedItem().equals("true")
275: && e.isAssociationEntity.getSelectedItem()
276: .equals("false")) {
277: // If composite is set to true,
278: // The primary key type should be set, the primary key field should be empty
279: // and the number of fields marked as primary key should be > 1.
280:
281: atLeastOneCompositePK = true;
282:
283: if (e.pKeyTypeText.getText() == null
284: || e.pKeyTypeText.getText().equals("")) {
285: // The primary key type is empty! This is an error!
286: logger
287: .log("Primary key type is empty, but should be set since composite primary key has been set to true for Entity EJB: "
288: + e.getRefName());
289: valid = false;
290: message += "Primary key type is empty, but should be set since composite primary key has been set to true for Entity EJB: "
291: + e.getRefName() + ".\r\n";
292: }
293:
294: if (e.pKeyText.getText() != null
295: && !e.pKeyText.getText().equals("")) {
296: // The primary key type is empty! This is an error!
297: logger
298: .log("Primary key should be empty since the since composite primary key has been set to true for Entity EJB: "
299: + e.getRefName());
300: valid = false;
301: message += "Primary key should be empty, since composite primary key has been set to true for Entity EJB: "
302: + e.getRefName() + ".\r\n";
303: }
304:
305: if (e.countPrimaryKeyFields() <= 1) {
306: logger
307: .log("In the field specifications for Entity EJB: "
308: + e.getRefName()
309: + " there should be more than one field that has been marked as primary key. Now only "
310: + e.countPrimaryKeyFields()
311: + " have been marked as primary key!");
312: valid = false;
313: message += "In the field specifications for Entity EJB: "
314: + e.getRefName()
315: + " there should be more than one field that has been marked as primary key. Now only "
316: + e.countPrimaryKeyFields()
317: + " have been marked as primary key!.\r\n";
318: }
319:
320: } else if ((e.isCompositeCombo.getSelectedItem())
321: .equals("false")) {
322: if (e.pKeyTypeText.getText() == null
323: || e.pKeyTypeText.getText().equals("")) {
324: logger
325: .log("No primary key set for entity bean: "
326: + e.getRefName());
327: valid = false;
328: message += "No primary key set for entity bean: "
329: + e.getRefName() + ".\r\n";
330: }
331:
332: if (e.countPrimaryKeyFields() > 1) {
333: // There should be just 1 primary key selected.
334: if (e.countPrimaryKeyFields() > 1) {
335: logger
336: .log("More than 1 primary key declared, while composite primary key is set to false for entity bean '"
337: + e.getRefName() + "' .");
338: valid = false;
339: message += "More than 1 primary key declared, while composite primary key is set to false for entity bean '"
340: + e.getRefName() + "' .\r\n";
341: }
342: }
343:
344: if (e.countPrimaryKeyFields() == 1) {
345: // In this case we can check if the specified primary field and type, match the selected field.
346: String name = e.getFirstPrimaryKeyFieldName();
347: String type = e.getPrimaryKeyClass();
348:
349: if ((name == null)
350: || e.pKeyText.getText() == null
351: || !name.equals(e.pKeyText.getText())) {
352: logger
353: .log("Non-matching primary keys! In entity bean '"
354: + e.getRefName()
355: + "' the field '"
356: + name
357: + "' has been marked as primary key, yet that entity has primary key '"
358: + e.pKeyText.getText()
359: + "'.");
360: valid = false;
361: message += "Non-matching primary keys! In entity bean '"
362: + e.getRefName()
363: + "' the field '"
364: + name
365: + "' has been marked as primary key, yet that entity has primary key '"
366: + e.pKeyText.getText() + "'.\r\n";
367: }
368: if ((type == null)
369: || e.pKeyTypeText.getText() == null
370: || !type.equals(e.pKeyTypeText
371: .getText())) {
372: logger
373: .log("Non-matching primary key types! In entity bean '"
374: + e.getRefName()
375: + "' the field '"
376: + name
377: + "' has been marked as primary key and has type '"
378: + type
379: + "', yet the entity has primary key type '"
380: + e.pKeyTypeText.getText()
381: + "'.");
382: valid = false;
383: message += "Non-matching primary key types! In entity bean '"
384: + e.getRefName()
385: + "' the field '"
386: + name
387: + "' has been marked as primary key and has type '"
388: + type
389: + "', yet the entity has primary key type '"
390: + e.pKeyTypeText.getText()
391: + "'.\r\n";
392: }
393: }
394: }
395: }
396:
397: } catch (Exception e) {
398: valid = false;
399: message += "Error in Entity Bean.\r\n";
400: }
401:
402: //check for duplicate relation names within the same entity bean..
403: String badEntity = getEntityWithDuplicateRelationNames();
404: if (badEntity != null) {
405: valid = false;
406: message += "Entity bean "
407: + badEntity
408: + " has more than one relation with the same 'relation name'.\n"
409: + "This is not allowed! Please rename at least one of the relation names.\r\n";
410: }
411:
412: //check for relations that reference tables for which there is no entity bean..
413: List unreferencedEntities = getEntityWithUnreferencedRelations();
414: if (!unreferencedEntities.isEmpty()) {
415: valid = false;
416: StringBuffer tmp = new StringBuffer();
417: Iterator i = unreferencedEntities.iterator();
418: while (i.hasNext()) {
419: String[] details = (String[]) i.next();
420: if (!"".equals(details[2])) {
421: tmp
422: .append("Entity bean '"
423: + details[0]
424: + "' contains a relation '"
425: + details[1]
426: + "' that references a table '"
427: + details[2]
428: + "',\n"
429: + "which doesn't correspond to any entity bean. Please either:\n"
430: + "\ta) delete the relation,\n"
431: + "\tb) create an entity bean for the table '"
432: + details[2]
433: + "', or\n"
434: + "\tc) in the relation, correct the 'foreign table' value.\r\n");
435: }
436: }
437: message += tmp.toString();
438: }
439:
440: //check for any relation with empty/invalid data..
441: String relationErrorMessage = getRelationWithInvalidData();
442: if (relationErrorMessage != null) {
443: valid = false;
444: message += relationErrorMessage;
445: }
446: // Check if the related entity is a composite primary key.
447: if (isRelatedEntityComposite) {
448: String msg = "At least one target entity of a relation has a composite primary key. JAG doesn't support this.\r\n";
449: logger.log(msg);
450: valid = false;
451: message += msg;
452: }
453: // check if the Container-managed relations checkbox was unchecked BUT some CMRs are still defined!
454: String templateValue = (String) root.config
455: .getTemplateSettings().get(
456: JagGenerator.TEMPLATE_USE_RELATIONS);
457: if ("false".equalsIgnoreCase(templateValue)) {
458: // CMR was not selected. Make sure no relations are defined in the model!
459:
460: // Now check if there are still some relations defined.
461: if (isRelationPresentInModel()) {
462: valid = false;
463: message += "The Container-managed relations checkbox in the Configuration screen was unchecked,\r\n"
464: + "but there are still relations defined in the project.\r\n"
465: + "Either enable Container-managed relations or remove the relations from the project.\r\n";
466: }
467: }
468:
469: // Check business tier specific validations.
470: if (atLeastOneCompositePK) {
471: // In case Hibernate 2 was selected, No composites are supported!
472: if (JagGenerator.TEMPLATE_BUSINESS_TIER_HIBERNATE2
473: .equalsIgnoreCase(businessTier)
474: || (JagGenerator.TEMPLATE_BUSINESS_TIER_HIBERNATE3
475: .equalsIgnoreCase(businessTier) && "false"
476: .equals(useJava5))) {
477: logger
478: .log("JAG does not support composite primary keys for Hibernate.");
479: valid = false;
480: message += "JAG does not support composite primary keys for Hibernate.\r\n";
481: }
482: }
483:
484: if (autoGeneratedStringInvalidHibernate) {
485: // In case Hibernate 2 was selected, autogenerated string's should be at least size 32
486: if (JagGenerator.TEMPLATE_BUSINESS_TIER_HIBERNATE2
487: .equalsIgnoreCase(businessTier)
488: || JagGenerator.TEMPLATE_BUSINESS_TIER_HIBERNATE3
489: .equalsIgnoreCase(businessTier)) {
490: String msg = "At least one autogenerated primary key field of type string has size < 32. Hibernate cannot autogenerate this primary key.\r\n";
491: logger.log(msg);
492: valid = false;
493: message += msg;
494: }
495: }
496:
497: // Check application server specific validations.
498:
499: if (JagGenerator.TEMPLATE_APPLICATION_SERVER_TOMCAT_5
500: .equalsIgnoreCase(appServer)) {
501: // Only Hibernate is supported for deployment on tomcat.
502: // All other business tiers are dependent on an EJB container.
503: if (!JagGenerator.TEMPLATE_BUSINESS_TIER_HIBERNATE2
504: .equalsIgnoreCase(businessTier)
505: && !JagGenerator.TEMPLATE_BUSINESS_TIER_HIBERNATE3
506: .equalsIgnoreCase(businessTier)) {
507: String msg = "Only hibernate can be selected for the Tomcat application server.\r\n";
508: logger.log(msg);
509: valid = false;
510: message += msg;
511: }
512:
513: }
514:
515: if (!JagGenerator.TEMPLATE_APPLICATION_SERVER_JBOSS_4_X
516: .equalsIgnoreCase(appServer)) {
517: if (JagGenerator.TEMPLATE_BUSINESS_TIER_EJB3
518: .equalsIgnoreCase(businessTier)) {
519: // EJB3 is currently only supported for JBoss 4.X
520: logger
521: .log("EJB3 is only supported for the JBoss 4.x application server.");
522: valid = false;
523: message += "EJB3 is only supported for the JBoss 4.x application server.\r\n";
524: }
525: }
526:
527: if (!valid) {
528: return message;
529: } else {
530: return null;
531: }
532: }
533:
534: private String getEntityWithDuplicateRelationNames() {
535: Iterator entities = root.getEntityEjbs().iterator();
536: while (entities.hasNext()) {
537: Entity entity = (Entity) entities.next();
538: Set relationNames = new HashSet();
539: for (int i = 0; i < entity.getChildCount(); i++) {
540: TreeNode child = entity.getChildAt(i);
541: if (child instanceof Relation) {
542: Relation relation = (Relation) child;
543: String relName = relation.getName();
544: if (relationNames.contains(relName)) {
545: tree.setSelectionPath(new TreePath(relation
546: .getPath()));
547: return entity.getRefName();
548: }
549: relationNames.add(relName);
550: }
551: }
552: }
553: return null;
554: }
555:
556: /**
557: * Finds any invalid relations, whose referenced foreign tables do not correspond to entity beans.
558: *
559: * @return List of String[] (never null), where:
560: * [0] is the name of the entity bean containing the relation
561: * [1] is the name of the relation
562: * [2] is the name of the referenced table, for which there is no entity bean.
563: */
564: private List getEntityWithUnreferencedRelations() {
565: ArrayList result = new ArrayList();
566: Iterator entities = root.getEntityEjbs().iterator();
567: while (entities.hasNext()) {
568: Entity entity = (Entity) entities.next();
569: for (int i = 0; i < entity.getChildCount(); i++) {
570: TreeNode child = entity.getChildAt(i);
571: if (child instanceof Relation) {
572: Relation relation = (Relation) child;
573: String referencedTable = relation.getForeignTable();
574: if (entitiesByTableName.get(referencedTable) == null) {
575: result.add(new String[] {
576: entity.getName().toString(),
577: relation.getName(), referencedTable });
578: }
579: }
580: }
581: }
582: return result;
583: }
584:
585: private String getRelationWithInvalidData() {
586: Iterator entities = root.getEntityEjbs().iterator();
587: while (entities.hasNext()) {
588: Entity entity = (Entity) entities.next();
589: for (int i = 0; i < entity.getChildCount(); i++) {
590: TreeNode child = entity.getChildAt(i);
591: if (child instanceof Relation) {
592: Relation relation = (Relation) child;
593:
594: String foreignTable = relation.getForeignTable();
595: if (foreignTable == null
596: || "".equals(foreignTable.trim())) {
597: return "Relation '"
598: + relation
599: + "' within entity '"
600: + entity.getName()
601: + "' has no value for 'foreign table' \n"
602: + "(the name of the table at the foreign side of this relation). \n"
603: + "Please provide a value!";
604: }
605:
606: if (entitiesByTableName.get(foreignTable) == null) {
607: return null;
608: //this is needed because otherwise this method throws an NPE.
609: //@todo Really should look at improving this validation/error handling mess...
610: }
611:
612: String foreignPkColumn = relation
613: .getForeignColumn();
614: if (foreignPkColumn == null
615: || "".equals(foreignPkColumn.trim())) {
616: return "Relation '"
617: + relation
618: + "' within entity '"
619: + entity.getName()
620: + "' has no value for 'foreign table primary key' \n"
621: + "(the name of the PK imported from table '"
622: + relation.getForeignTable()
623: + "' that represents the foreign side of this relation). \n"
624: + "Please provide a value!";
625: }
626:
627: boolean found = false;
628: List fields = relation.getRelatedEntity()
629: .getFields();
630: Iterator j = fields.iterator();
631: while (j.hasNext()) {
632: Field field = (Field) j.next();
633: if (field.getColumnName().equalsIgnoreCase(
634: foreignPkColumn)) {
635: found = true;
636: break;
637: }
638: }
639:
640: if (!found) {
641: return "Relation '"
642: + relation
643: + "' within entity '"
644: + relation.getRelatedEntity().getName()
645: + "' has an invalid value ("
646: + foreignPkColumn
647: + ") for 'foreign table primary key' \n"
648: + "(the name of the PK imported from table '"
649: + relation.getRelatedEntity()
650: .getLocalTableName()
651: + "' that represents the foreign side of this relation). \n"
652: + "Please provide a valid value!";
653: }
654:
655: }
656: }
657: }
658: return null;
659: }
660:
661: /**
662: * Helper method to check if there are any relations present in the model.
663: */
664: private boolean isRelationPresentInModel() {
665: Iterator entities = root.getEntityEjbs().iterator();
666: while (entities.hasNext()) {
667: Entity entity = (Entity) entities.next();
668: //for every entity..
669: for (int i = 0; i < entity.getChildCount(); i++) {
670: TreeNode child = entity.getChildAt(i);
671: //for every relation in that entity..
672: if (child instanceof Relation) {
673: return true;
674: }
675: }
676: }
677: return false;
678: }
679:
680: }
|