Source Code Cross Referenced for Relation.java in  » Web-Framework » helma » helma » objectmodel » db » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Web Framework » helma » helma.objectmodel.db 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Helma License Notice
0003:         *
0004:         * The contents of this file are subject to the Helma License
0005:         * Version 2.0 (the "License"). You may not use this file except in
0006:         * compliance with the License. A copy of the License is available at
0007:         * http://adele.helma.org/download/helma/license.txt
0008:         *
0009:         * Copyright 1998-2003 Helma Software. All Rights Reserved.
0010:         *
0011:         * $RCSfile$
0012:         * $Author: hannes $
0013:         * $Revision: 8704 $
0014:         * $Date: 2007-12-11 17:16:28 +0100 (Die, 11 Dez 2007) $
0015:         */
0016:
0017:        package helma.objectmodel.db;
0018:
0019:        import helma.framework.core.Application;
0020:        import helma.objectmodel.INode;
0021:        import helma.objectmodel.IProperty;
0022:        import helma.util.StringUtils;
0023:        import helma.util.ResourceProperties;
0024:
0025:        import java.sql.SQLException;
0026:        import java.util.HashMap;
0027:        import java.util.Map;
0028:        import java.util.Properties;
0029:        import java.util.Enumeration;
0030:        import java.util.Vector;
0031:
0032:        /**
0033:         * This describes how a property of a persistent Object is stored in a
0034:         *  relational database table. This can be either a scalar property (string, date, number etc.)
0035:         *  or a reference to one or more other objects.
0036:         */
0037:        public final class Relation {
0038:            // these constants define different type of property-to-db-mappings
0039:            // there is an error in the description of this relation
0040:            public final static int INVALID = -1;
0041:
0042:            // a mapping of a non-object, scalar type
0043:            public final static int PRIMITIVE = 0;
0044:
0045:            // a 1-to-1 relation, i.e. a field in the table is a foreign key to another object
0046:            public final static int REFERENCE = 1;
0047:
0048:            // a 1-to-many relation, a field in another table points to objects of this type
0049:            public final static int COLLECTION = 2;
0050:
0051:            // a 1-to-1 reference with multiple or otherwise not-trivial constraints
0052:            // this is managed differently than REFERENCE, hence the separate type.
0053:            public final static int COMPLEX_REFERENCE = 3;
0054:
0055:            // constraints linked together by OR or AND if applicable?
0056:            public final static String AND = " AND ";
0057:            public final static String OR = " OR ";
0058:            public final static String XOR = " XOR ";
0059:            private String logicalOperator = AND;
0060:
0061:            // prefix to use for symbolic names of joined tables. The name is composed
0062:            // from this prefix and the name of the property we're doing the join for
0063:            final static String JOIN_PREFIX = "JOIN_";
0064:
0065:            // direct mapping is a very powerful feature:
0066:            // objects of some types can be directly accessed
0067:            // by one of their properties/db fields.
0068:            // public final static int DIRECT = 3;
0069:            // the DbMapping of the type we come from
0070:            DbMapping ownType;
0071:
0072:            // the DbMapping of the prototype we link to, unless this is a "primitive" (non-object) relation
0073:            DbMapping otherType;
0074:
0075:            // the column type, as defined in java.sql.Types
0076:            int columnType;
0077:
0078:            //  if this relation defines a virtual node, we need to provide a DbMapping for these virtual nodes
0079:            DbMapping virtualMapping;
0080:            String propName;
0081:            String columnName;
0082:            int reftype;
0083:            Constraint[] constraints;
0084:            boolean virtual;
0085:            boolean readonly;
0086:            boolean aggressiveLoading;
0087:            boolean aggressiveCaching;
0088:            boolean isPrivate = false;
0089:            boolean referencesPrimaryKey = false;
0090:            String updateCriteria;
0091:            String accessName; // db column used to access objects through this relation
0092:            String order;
0093:            boolean autoSorted = false;
0094:            String groupbyOrder;
0095:            String groupby;
0096:            String prototype;
0097:            String groupbyPrototype;
0098:            String filter;
0099:            private String additionalTables;
0100:            private boolean additionalTablesJoined = false;
0101:            String queryHints;
0102:            Vector filterFragments;
0103:            Vector filterPropertyRefs;
0104:            int maxSize = 0;
0105:
0106:            /**
0107:             * This constructor makes a copy of an existing relation. Not all fields are copied, just those
0108:             * which are needed in groupby- and virtual nodes defined by this relation.
0109:             */
0110:            private Relation(Relation rel) {
0111:                // Note: prototype, groupby, groupbyPrototype and groupbyOrder aren't copied here.
0112:                // these are set by the individual get*Relation() methods as appropriate.
0113:                this .ownType = rel.ownType;
0114:                this .otherType = rel.otherType;
0115:                this .propName = rel.propName;
0116:                this .columnName = rel.columnName;
0117:                this .reftype = rel.reftype;
0118:                this .order = rel.order;
0119:                this .filter = rel.filter;
0120:                this .filterFragments = rel.filterFragments;
0121:                this .filterPropertyRefs = rel.filterPropertyRefs;
0122:                this .additionalTables = rel.additionalTables;
0123:                this .additionalTablesJoined = rel.additionalTablesJoined;
0124:                this .queryHints = rel.queryHints;
0125:                this .maxSize = rel.maxSize;
0126:                this .constraints = rel.constraints;
0127:                this .accessName = rel.accessName;
0128:                this .maxSize = rel.maxSize;
0129:                this .logicalOperator = rel.logicalOperator;
0130:                this .aggressiveLoading = rel.aggressiveLoading;
0131:                this .aggressiveCaching = rel.aggressiveCaching;
0132:                this .updateCriteria = rel.updateCriteria;
0133:                this .autoSorted = rel.autoSorted;
0134:            }
0135:
0136:            /**
0137:             * Reads a relation entry from a line in a properties file.
0138:             */
0139:            public Relation(String propName, DbMapping ownType) {
0140:                this .ownType = ownType;
0141:                this .propName = propName;
0142:                otherType = null;
0143:            }
0144:
0145:            ////////////////////////////////////////////////////////////////////////////////////////////
0146:            // parse methods for new file format
0147:            ////////////////////////////////////////////////////////////////////////////////////////////
0148:            public void update(String desc, ResourceProperties props) {
0149:                Application app = ownType.getApplication();
0150:
0151:                if ((desc == null) || "".equals(desc.trim())) {
0152:                    if (propName != null) {
0153:                        reftype = PRIMITIVE;
0154:                        columnName = propName;
0155:                    } else {
0156:                        reftype = INVALID;
0157:                        columnName = propName;
0158:                    }
0159:                } else {
0160:                    desc = desc.trim();
0161:
0162:                    int open = desc.indexOf("(");
0163:                    int close = desc.indexOf(")");
0164:
0165:                    if ((open > -1) && (close > open)) {
0166:                        String ref = desc.substring(0, open).trim();
0167:                        String proto = desc.substring(open + 1, close).trim();
0168:
0169:                        if ("collection".equalsIgnoreCase(ref)) {
0170:                            virtual = !"_children".equalsIgnoreCase(propName);
0171:                            reftype = COLLECTION;
0172:                        } else if ("mountpoint".equalsIgnoreCase(ref)) {
0173:                            virtual = true;
0174:                            reftype = COLLECTION;
0175:                            prototype = proto;
0176:                        } else if ("object".equalsIgnoreCase(ref)) {
0177:                            virtual = false;
0178:                            if (reftype != COMPLEX_REFERENCE) {
0179:                                reftype = REFERENCE;
0180:                            }
0181:                        } else {
0182:                            throw new RuntimeException(
0183:                                    "Invalid property Mapping: " + desc);
0184:                        }
0185:
0186:                        otherType = app.getDbMapping(proto);
0187:
0188:                        if (otherType == null) {
0189:                            throw new RuntimeException("DbMapping for " + proto
0190:                                    + " not found from "
0191:                                    + ownType.getTypeName());
0192:                        }
0193:
0194:                        // make sure the type we're referring to is up to date!
0195:                        if (otherType.needsUpdate()) {
0196:                            otherType.update();
0197:                        }
0198:
0199:                    } else {
0200:                        virtual = false;
0201:                        columnName = desc;
0202:                        reftype = PRIMITIVE;
0203:                    }
0204:                }
0205:
0206:                ResourceProperties config = props
0207:                        .getSubProperties(propName + '.');
0208:
0209:                readonly = "true".equalsIgnoreCase(config
0210:                        .getProperty("readonly"));
0211:
0212:                isPrivate = "true".equalsIgnoreCase(config
0213:                        .getProperty("private"));
0214:
0215:                // the following options only apply to object and collection relations
0216:                if ((reftype != PRIMITIVE) && (reftype != INVALID)) {
0217:                    Vector newConstraints = new Vector();
0218:
0219:                    parseOptions(newConstraints, config);
0220:
0221:                    constraints = new Constraint[newConstraints.size()];
0222:                    newConstraints.copyInto(constraints);
0223:
0224:                    if (reftype == REFERENCE || reftype == COMPLEX_REFERENCE) {
0225:                        if (constraints.length == 0) {
0226:                            referencesPrimaryKey = true;
0227:                        } else {
0228:                            boolean rprim = false;
0229:                            for (int i = 0; i < constraints.length; i++) {
0230:                                if (constraints[i].foreignKeyIsPrimary()) {
0231:                                    rprim = true;
0232:                                    break;
0233:                                }
0234:                            }
0235:                            referencesPrimaryKey = rprim;
0236:                        }
0237:
0238:                        // check if this is a non-trivial reference
0239:                        if (constraints.length > 1 || !usesPrimaryKey()) {
0240:                            reftype = COMPLEX_REFERENCE;
0241:                        } else {
0242:                            reftype = REFERENCE;
0243:                        }
0244:                    }
0245:
0246:                    if (reftype == COLLECTION) {
0247:                        referencesPrimaryKey = (accessName == null)
0248:                                || accessName.equalsIgnoreCase(otherType
0249:                                        .getIDField());
0250:                    }
0251:
0252:                    // if DbMapping for virtual nodes has already been created,
0253:                    // update its subnode relation.
0254:                    // FIXME: needs to be synchronized?
0255:                    if (virtualMapping != null) {
0256:                        virtualMapping.lastTypeChange = ownType.lastTypeChange;
0257:                        virtualMapping.subRelation = getVirtualSubnodeRelation();
0258:                        virtualMapping.propRelation = getVirtualPropertyRelation();
0259:                    }
0260:                } else {
0261:                    referencesPrimaryKey = false;
0262:                }
0263:            }
0264:
0265:            protected void parseOptions(Vector cnst, Properties config) {
0266:                String loading = config.getProperty("loadmode");
0267:
0268:                aggressiveLoading = (loading != null)
0269:                        && "aggressive".equalsIgnoreCase(loading.trim());
0270:
0271:                String caching = config.getProperty("cachemode");
0272:
0273:                aggressiveCaching = (caching != null)
0274:                        && "aggressive".equalsIgnoreCase(caching.trim());
0275:
0276:                // get order property
0277:                order = config.getProperty("order");
0278:
0279:                if ((order != null) && (order.trim().length() == 0)) {
0280:                    order = null;
0281:                }
0282:
0283:                // get the criteria(s) for updating this collection
0284:                updateCriteria = config.getProperty("updatecriteria");
0285:
0286:                // get the autosorting flag
0287:                autoSorted = "auto".equalsIgnoreCase(config
0288:                        .getProperty("sortmode"));
0289:
0290:                // get additional filter property
0291:                filter = config.getProperty("filter");
0292:
0293:                if (filter != null) {
0294:                    if (filter.trim().length() == 0) {
0295:                        filter = null;
0296:                        filterFragments = filterPropertyRefs = null;
0297:                    } else {
0298:                        // parenthesise filter
0299:                        Vector fragments = new Vector();
0300:                        Vector propertyRefs = new Vector();
0301:                        parsePropertyString(filter, fragments, propertyRefs);
0302:                        // if no references where found, just use the filter string
0303:                        // otherwise use the filter fragments and proeprty refs instead
0304:                        if (propertyRefs.size() > 0) {
0305:                            filterFragments = fragments;
0306:                            filterPropertyRefs = propertyRefs;
0307:                        } else {
0308:                            filterFragments = filterPropertyRefs = null;
0309:                        }
0310:                    }
0311:                }
0312:
0313:                // get additional tables
0314:                additionalTables = config
0315:                        .getProperty("filter.additionalTables");
0316:
0317:                if (additionalTables != null) {
0318:                    if (additionalTables.trim().length() == 0) {
0319:                        additionalTables = null;
0320:                    } else {
0321:                        String ucTables = additionalTables.toUpperCase();
0322:                        // create dependencies implied by additional tables
0323:                        DbSource dbsource = otherType.getDbSource();
0324:                        if (dbsource != null) {
0325:                            String[] tables = StringUtils.split(ucTables, ", ");
0326:                            for (int i = 0; i < tables.length; i++) {
0327:                                // Skip some join-related keyworks we might encounter here
0328:                                if ("AS".equals(tables[i])
0329:                                        || "ON".equals(tables[i])) {
0330:                                    continue;
0331:                                }
0332:                                DbMapping dbmap = dbsource
0333:                                        .getDbMapping(tables[i]);
0334:                                if (dbmap != null) {
0335:                                    dbmap.addDependency(otherType);
0336:                                }
0337:                            }
0338:                        }
0339:                        // see wether the JOIN syntax is used. look for " join " with whitespaces on both sides
0340:                        // and for "join " at the beginning:
0341:                        additionalTablesJoined = (ucTables.indexOf(" JOIN ") != -1
0342:                                || ucTables.startsWith("STRAIGHT_JOIN ") || ucTables
0343:                                .startsWith("JOIN "));
0344:                    }
0345:                }
0346:
0347:                // get query hints
0348:                queryHints = config.getProperty("hints");
0349:
0350:                // get max size of collection
0351:                String max = config.getProperty("maxSize");
0352:
0353:                if (max != null) {
0354:                    try {
0355:                        maxSize = Integer.parseInt(max);
0356:                    } catch (NumberFormatException nfe) {
0357:                        maxSize = 0;
0358:                    }
0359:                } else {
0360:                    maxSize = 0;
0361:                }
0362:
0363:                // get group by property
0364:                groupby = config.getProperty("group");
0365:
0366:                if ((groupby != null) && (groupby.trim().length() == 0)) {
0367:                    groupby = null;
0368:                }
0369:
0370:                if (groupby != null) {
0371:                    groupbyOrder = config.getProperty("group.order");
0372:
0373:                    if ((groupbyOrder != null)
0374:                            && (groupbyOrder.trim().length() == 0)) {
0375:                        groupbyOrder = null;
0376:                    }
0377:
0378:                    groupbyPrototype = config.getProperty("group.prototype");
0379:
0380:                    if ((groupbyPrototype != null)
0381:                            && (groupbyPrototype.trim().length() == 0)) {
0382:                        groupbyPrototype = null;
0383:                    }
0384:
0385:                    // aggressive loading and caching is not supported for groupby-nodes
0386:                    // aggressiveLoading = aggressiveCaching = false;
0387:                }
0388:
0389:                // check if subnode condition should be applied for property relations
0390:                accessName = config.getProperty("accessname");
0391:
0392:                // parse contstraints
0393:                String local = config.getProperty("local");
0394:                String foreign = config.getProperty("foreign");
0395:
0396:                if ((local != null) && (foreign != null)) {
0397:                    cnst.addElement(new Constraint(local, foreign, false));
0398:                    columnName = local;
0399:                }
0400:
0401:                // parse additional contstraints from *.1 to *.9
0402:                for (int i = 1; i < 10; i++) {
0403:                    local = config.getProperty("local." + i);
0404:                    foreign = config.getProperty("foreign." + i);
0405:
0406:                    if ((local != null) && (foreign != null)) {
0407:                        cnst.addElement(new Constraint(local, foreign, false));
0408:                    }
0409:                }
0410:
0411:                // parse constraints logic
0412:                if (cnst.size() > 1) {
0413:                    String logic = config.getProperty("logicalOperator");
0414:                    if ("and".equalsIgnoreCase(logic)) {
0415:                        logicalOperator = AND;
0416:                    } else if ("or".equalsIgnoreCase(logic)) {
0417:                        logicalOperator = OR;
0418:                    } else if ("xor".equalsIgnoreCase(logic)) {
0419:                        logicalOperator = XOR;
0420:                    } else {
0421:                        logicalOperator = AND;
0422:                    }
0423:                } else {
0424:                    logicalOperator = AND;
0425:                }
0426:
0427:            }
0428:
0429:            ///////////////////////////////////////////////////////////////////////////////////////////
0430:
0431:            /**
0432:             * Get the configuration properties for this relation.
0433:             */
0434:            public ResourceProperties getConfig() {
0435:                return ownType.getProperties().getSubProperties(propName + '.');
0436:            }
0437:
0438:            /**
0439:             * Does this relation describe a virtual (collection) node?
0440:             */
0441:            public boolean isVirtual() {
0442:                return virtual;
0443:            }
0444:
0445:            /**
0446:             * Return the target type of this relation, or null if this is a primitive mapping.
0447:             */
0448:            public DbMapping getTargetType() {
0449:                return otherType;
0450:            }
0451:
0452:            /**
0453:             * Get the reference type of this relation.
0454:             */
0455:            public int getRefType() {
0456:                return reftype;
0457:            }
0458:
0459:            /**
0460:             * Tell if this relation represents a primitive (scalar) value mapping.
0461:             */
0462:            public boolean isPrimitive() {
0463:                return reftype == PRIMITIVE;
0464:            }
0465:
0466:            /**
0467:             *  Returns true if this Relation describes an object reference property
0468:             */
0469:            public boolean isReference() {
0470:                return reftype == REFERENCE;
0471:            }
0472:
0473:            /**
0474:             *  Returns true if this Relation describes either a primitive value
0475:             *  or an object reference.
0476:             */
0477:            public boolean isPrimitiveOrReference() {
0478:                return reftype == PRIMITIVE || reftype == REFERENCE;
0479:            }
0480:
0481:            /**
0482:             *  Returns true if this Relation describes a collection.
0483:             *  <b>NOTE:</b> this will return true both for collection objects
0484:             *  (aka virtual nodes) and direct child object relations, so
0485:             *  isVirtual() should be used to identify relations that define
0486:             *  <i>collection properties</i>!
0487:             */
0488:            public boolean isCollection() {
0489:                return reftype == COLLECTION;
0490:            }
0491:
0492:            /**
0493:             *  Returns true if this Relation describes a complex object reference property
0494:             */
0495:            public boolean isComplexReference() {
0496:                return reftype == COMPLEX_REFERENCE;
0497:            }
0498:
0499:            /**
0500:             *  Tell wether the property described by this relation is to be handled as private, i.e.
0501:             *  a change on it should not result in any changed object/collection relations.
0502:             */
0503:            public boolean isPrivate() {
0504:                return isPrivate;
0505:            }
0506:
0507:            /**
0508:             *  Check whether aggressive loading is set for this relation
0509:             */
0510:            public boolean loadAggressively() {
0511:                return aggressiveLoading;
0512:            }
0513:
0514:            /**
0515:             *  Returns the number of constraints for this relation.
0516:             */
0517:            public int countConstraints() {
0518:                if (constraints == null)
0519:                    return 0;
0520:                return constraints.length;
0521:            }
0522:
0523:            /**
0524:             *  Returns true if the object represented by this Relation has to be
0525:             *  created on demand at runtime by the NodeManager. This is true for:
0526:             *
0527:             *  - collection (aka virtual) nodes
0528:             *  - nodes accessed via accessname
0529:             *  - group nodes
0530:             *  - complex reference nodes
0531:             */
0532:            public boolean createOnDemand() {
0533:                if (otherType == null) {
0534:                    return false;
0535:                }
0536:
0537:                return virtual
0538:                        || (otherType.isRelational() && accessName != null)
0539:                        || (groupby != null) || isComplexReference();
0540:            }
0541:
0542:            /**
0543:             *  Returns true if the object represented by this Relation has to be
0544:             *  persisted in the internal db in order to be functional. This is true if
0545:             *  the subnodes contained in this collection are stored in the embedded
0546:             *  database. In this case, the collection itself must also be an ordinary
0547:             *  object stored in the db, since a virtual collection would lose its
0548:             *  its content after restarts.
0549:             */
0550:            public boolean needsPersistence() {
0551:                if (!virtual) {
0552:                    // ordinary object references always need to be persisted
0553:                    return true;
0554:                }
0555:
0556:                // collections/mountpoints need to be persisted if the
0557:                // child object type is non-relational.
0558:                if (prototype == null) {
0559:                    return !otherType.isRelational();
0560:                }
0561:
0562:                DbMapping sub = otherType.getSubnodeMapping();
0563:
0564:                return (sub != null) && !sub.isRelational();
0565:            }
0566:
0567:            /**
0568:             * Return the prototype to be used for object reached by this relation
0569:             */
0570:            public String getPrototype() {
0571:                return prototype;
0572:            }
0573:
0574:            /**
0575:             * Return the name of the local property this relation is defined for
0576:             */
0577:            public String getPropName() {
0578:                return propName;
0579:            }
0580:
0581:            /**
0582:             *
0583:             *
0584:             * @param ct ...
0585:             */
0586:            public void setColumnType(int ct) {
0587:                columnType = ct;
0588:            }
0589:
0590:            /**
0591:             *
0592:             *
0593:             * @return ...
0594:             */
0595:            public int getColumnType() {
0596:                return columnType;
0597:            }
0598:
0599:            /**
0600:             *  Get the group for a collection relation, if defined.
0601:             *
0602:             * @return the name of the column used to group child objects, if any.
0603:             */
0604:            public String getGroup() {
0605:                return groupby;
0606:            }
0607:
0608:            /**
0609:             * Add a constraint to the current list of constraints
0610:             */
0611:            protected void addConstraint(Constraint c) {
0612:                if (constraints == null) {
0613:                    constraints = new Constraint[1];
0614:                    constraints[0] = c;
0615:                } else {
0616:                    Constraint[] nc = new Constraint[constraints.length + 1];
0617:
0618:                    System.arraycopy(constraints, 0, nc, 0, constraints.length);
0619:                    nc[nc.length - 1] = c;
0620:                    constraints = nc;
0621:                }
0622:            }
0623:
0624:            /**
0625:             *
0626:             *
0627:             * @return true if the foreign key used for this relation is the
0628:             * other object's primary key.
0629:             */
0630:            public boolean usesPrimaryKey() {
0631:                return referencesPrimaryKey;
0632:            }
0633:
0634:            /**
0635:             *
0636:             *
0637:             * @return ...
0638:             */
0639:            public boolean hasAccessName() {
0640:                return accessName != null;
0641:            }
0642:
0643:            /**
0644:             *
0645:             *
0646:             * @return ...
0647:             */
0648:            public String getAccessName() {
0649:                return accessName;
0650:            }
0651:
0652:            /**
0653:             *
0654:             *
0655:             * @return ...
0656:             */
0657:            public Relation getSubnodeRelation() {
0658:                // return subnoderelation;
0659:                return null;
0660:            }
0661:
0662:            /**
0663:             * Return the local field name for updates.
0664:             */
0665:            public String getDbField() {
0666:                return columnName;
0667:            }
0668:
0669:            /**
0670:             * This is taken from org.apache.tools.ant ProjectHelper.java
0671:             * distributed under the Apache Software License, Version 1.1
0672:             *
0673:             * Parses a string containing <code>${xxx}</code> style property
0674:             * references into two lists. The first list is a collection
0675:             * of text fragments, while the other is a set of string property names.
0676:             * <code>null</code> entries in the first list indicate a property
0677:             * reference from the second list.
0678:             *
0679:             * @param value     Text to parse. Must not be <code>null</code>.
0680:             * @param fragments List to add text fragments to.
0681:             *                  Must not be <code>null</code>.
0682:             * @param propertyRefs List to add property names to.
0683:             *                     Must not be <code>null</code>.
0684:             */
0685:            protected void parsePropertyString(String value, Vector fragments,
0686:                    Vector propertyRefs) {
0687:                int prev = 0;
0688:                int pos;
0689:                //search for the next instance of $ from the 'prev' position
0690:                while ((pos = value.indexOf("$", prev)) >= 0) {
0691:
0692:                    //if there was any text before this, add it as a fragment
0693:                    //TODO, this check could be modified to go if pos>prev;
0694:                    //seems like this current version could stick empty strings
0695:                    //into the list
0696:                    if (pos > 0) {
0697:                        fragments.addElement(value.substring(prev, pos));
0698:                    }
0699:                    //if we are at the end of the string, we tack on a $
0700:                    //then move past it
0701:                    if (pos == (value.length() - 1)) {
0702:                        fragments.addElement("$");
0703:                        prev = pos + 1;
0704:                    } else if (value.charAt(pos + 1) != '{') {
0705:                        //peek ahead to see if the next char is a property or not
0706:                        //not a property: insert the char as a literal
0707:                        /*
0708:                        fragments.addElement(value.substring(pos + 1, pos + 2));
0709:                        prev = pos + 2;
0710:                         */
0711:                        if (value.charAt(pos + 1) == '$') {
0712:                            //backwards compatibility two $ map to one mode
0713:                            fragments.addElement("$");
0714:                            prev = pos + 2;
0715:                        } else {
0716:                            //new behaviour: $X maps to $X for all values of X!='$'
0717:                            fragments.addElement(value.substring(pos, pos + 2));
0718:                            prev = pos + 2;
0719:                        }
0720:
0721:                    } else {
0722:                        //property found, extract its name or bail on a typo
0723:                        int endName = value.indexOf('}', pos);
0724:                        if (endName < 0) {
0725:                            throw new RuntimeException(
0726:                                    "Syntax error in property: " + value);
0727:                        }
0728:                        String propertyName = value.substring(pos + 2, endName);
0729:                        fragments.addElement(null);
0730:                        propertyRefs.addElement(propertyName);
0731:                        prev = endName + 1;
0732:                    }
0733:                }
0734:                //no more $ signs found
0735:                //if there is any tail to the file, append it
0736:                if (prev < value.length()) {
0737:                    fragments.addElement(value.substring(prev));
0738:                }
0739:            }
0740:
0741:            /**
0742:             *  get a DbMapping to use for virtual aka collection nodes.
0743:             */
0744:            public DbMapping getVirtualMapping() {
0745:                // return null unless this relation describes a virtual/collection node.
0746:                if (!virtual) {
0747:                    return null;
0748:                }
0749:
0750:                // create a synthetic DbMapping that describes how to fetch the
0751:                // collection's child objects.
0752:                if (virtualMapping == null) {
0753:                    // if the collection node is prototyped (a mountpoint), create
0754:                    // a virtual sub-mapping from the app's DbMapping for that prototype
0755:                    if (prototype != null) {
0756:                        virtualMapping = new DbMapping(ownType.app, prototype);
0757:                    } else {
0758:                        virtualMapping = new DbMapping(ownType.app, null);
0759:                        virtualMapping.subRelation = getVirtualSubnodeRelation();
0760:                        virtualMapping.propRelation = getVirtualPropertyRelation();
0761:                    }
0762:                }
0763:
0764:                return virtualMapping;
0765:            }
0766:
0767:            /**
0768:             * Return the db mapping for a propery relation.
0769:             * @return the target mapping of this property relation
0770:             */
0771:            public DbMapping getPropertyMapping() {
0772:                // if this is an untyped virtual node, it doesn't have a dbmapping
0773:                if (!virtual || prototype != null) {
0774:                    return otherType;
0775:                }
0776:                return null;
0777:            }
0778:
0779:            /**
0780:             * Return a Relation that defines the subnodes of a virtual node.
0781:             */
0782:            Relation getVirtualSubnodeRelation() {
0783:                if (!virtual) {
0784:                    throw new RuntimeException(
0785:                            "getVirtualSubnodeRelation called on non-virtual relation");
0786:                }
0787:
0788:                Relation vr = new Relation(this );
0789:
0790:                vr.groupby = groupby;
0791:                vr.groupbyOrder = groupbyOrder;
0792:                vr.groupbyPrototype = groupbyPrototype;
0793:
0794:                return vr;
0795:            }
0796:
0797:            /**
0798:             * Return a Relation that defines the properties of a virtual node.
0799:             */
0800:            Relation getVirtualPropertyRelation() {
0801:                if (!virtual) {
0802:                    throw new RuntimeException(
0803:                            "getVirtualPropertyRelation called on non-virtual relation");
0804:                }
0805:
0806:                Relation vr = new Relation(this );
0807:
0808:                vr.groupby = groupby;
0809:                vr.groupbyOrder = groupbyOrder;
0810:                vr.groupbyPrototype = groupbyPrototype;
0811:
0812:                return vr;
0813:            }
0814:
0815:            /**
0816:             * Return a Relation that defines the subnodes of a group-by node.
0817:             */
0818:            Relation getGroupbySubnodeRelation() {
0819:                if (groupby == null) {
0820:                    throw new RuntimeException(
0821:                            "getGroupbySubnodeRelation called on non-group-by relation");
0822:                }
0823:
0824:                Relation vr = new Relation(this );
0825:
0826:                vr.prototype = groupbyPrototype;
0827:                vr.addConstraint(new Constraint(null, groupby, true));
0828:
0829:                return vr;
0830:            }
0831:
0832:            /**
0833:             * Return a Relation that defines the properties of a group-by node.
0834:             */
0835:            Relation getGroupbyPropertyRelation() {
0836:                if (groupby == null) {
0837:                    throw new RuntimeException(
0838:                            "getGroupbyPropertyRelation called on non-group-by relation");
0839:                }
0840:
0841:                Relation vr = new Relation(this );
0842:
0843:                vr.prototype = groupbyPrototype;
0844:                vr.addConstraint(new Constraint(null, groupby, true));
0845:
0846:                return vr;
0847:            }
0848:
0849:            /**
0850:             *  Build the second half of an SQL select statement according to this relation
0851:             *  and a local object.
0852:             */
0853:            public String buildQuery(INode home, INode nonvirtual, String kstr,
0854:                    String pre, boolean useOrder) throws SQLException,
0855:                    ClassNotFoundException {
0856:                return buildQuery(home, nonvirtual, otherType, kstr, pre,
0857:                        useOrder);
0858:            }
0859:
0860:            /**
0861:             *  Build the second half of an SQL select statement according to this relation
0862:             *  and a local object.
0863:             */
0864:            public String buildQuery(INode home, INode nonvirtual,
0865:                    DbMapping otherDbm, String kstr, String pre,
0866:                    boolean useOrder) throws SQLException,
0867:                    ClassNotFoundException {
0868:                StringBuffer q = new StringBuffer();
0869:                String prefix = pre;
0870:
0871:                if (kstr != null && !isComplexReference()) {
0872:                    q.append(prefix);
0873:
0874:                    String accessColumn = (accessName == null) ? otherDbm
0875:                            .getIDField() : accessName;
0876:                    otherDbm.appendCondition(q, accessColumn, kstr);
0877:
0878:                    prefix = " AND ";
0879:                }
0880:
0881:                // render the constraints and filter
0882:                renderConstraints(q, home, nonvirtual, otherDbm, prefix);
0883:
0884:                // add joined fetch constraints
0885:                ownType.addJoinConstraints(q, prefix);
0886:
0887:                // add group and order clauses
0888:                if (groupby != null) {
0889:                    q.append(" GROUP BY ").append(groupby);
0890:
0891:                    if (useOrder && (groupbyOrder != null)) {
0892:                        q.append(" ORDER BY ").append(groupbyOrder);
0893:                    }
0894:                } else if (useOrder && (order != null)) {
0895:                    q.append(" ORDER BY ").append(order);
0896:                }
0897:
0898:                return q.toString();
0899:            }
0900:
0901:            protected void appendAdditionalTables(StringBuffer q) {
0902:                if (additionalTables != null) {
0903:                    q.append(additionalTablesJoined ? ' ' : ',');
0904:                    q.append(additionalTables);
0905:                }
0906:            }
0907:
0908:            /**
0909:             *  Build the filter.
0910:             */
0911:            protected void appendFilter(StringBuffer q, INode nonvirtual,
0912:                    String prefix) {
0913:                q.append(prefix);
0914:                q.append('(');
0915:                if (filterFragments == null) {
0916:                    q.append(filter);
0917:                } else {
0918:                    Enumeration i = filterFragments.elements();
0919:                    Enumeration j = filterPropertyRefs.elements();
0920:                    while (i.hasMoreElements()) {
0921:                        String fragment = (String) i.nextElement();
0922:                        if (fragment == null) {
0923:                            // begin column version
0924:                            String columnName = (String) j.nextElement();
0925:                            Object value = null;
0926:                            if (columnName != null) {
0927:                                DbMapping dbmap = nonvirtual.getDbMapping();
0928:                                String propertyName = dbmap
0929:                                        .columnNameToProperty(columnName);
0930:                                if (propertyName == null)
0931:                                    propertyName = columnName;
0932:                                IProperty property = nonvirtual
0933:                                        .get(propertyName);
0934:                                if (property != null) {
0935:                                    value = property.getStringValue();
0936:                                }
0937:                                if (value == null) {
0938:                                    if (columnName.equalsIgnoreCase(dbmap
0939:                                            .getIDField())) {
0940:                                        value = nonvirtual.getID();
0941:                                    } else if (columnName
0942:                                            .equalsIgnoreCase(dbmap
0943:                                                    .getNameField())) {
0944:                                        value = nonvirtual.getName();
0945:                                    } else if (columnName
0946:                                            .equalsIgnoreCase(dbmap
0947:                                                    .getPrototypeField())) {
0948:                                        value = dbmap.getExtensionId();
0949:                                    }
0950:                                }
0951:                            }
0952:                            // end column version
0953:                            if (value != null) {
0954:                                q.append(DbMapping.escapeString(value
0955:                                        .toString()));
0956:                            } else {
0957:                                q.append("NULL");
0958:                            }
0959:                        } else {
0960:                            q.append(fragment);
0961:                        }
0962:                    }
0963:                }
0964:                q.append(')');
0965:            }
0966:
0967:            /**
0968:             * Render contraints and filter conditions to an SQL query string buffer.
0969:             *
0970:             * @param q the query string
0971:             * @param home our home node
0972:             * @param nonvirtual our non-virtual home node
0973:             * @param prefix the prefix to use to append to the existing query (e.g. " AND ")
0974:             *
0975:             * @throws SQLException sql related exception
0976:             * @throws ClassNotFoundException driver class not found
0977:             */
0978:            public void renderConstraints(StringBuffer q, INode home,
0979:                    INode nonvirtual, String prefix) throws SQLException,
0980:                    ClassNotFoundException {
0981:                renderConstraints(q, home, nonvirtual, otherType, prefix);
0982:            }
0983:
0984:            /**
0985:             * Render contraints and filter conditions to an SQL query string buffer.
0986:             *
0987:             * @param q the query string
0988:             * @param home our home node
0989:             * @param nonvirtual our non-virtual home nod
0990:             * @param otherDbm the DbMapping of the remote Node
0991:             * @param prefix the prefix to use to append to the existing query (e.g. " AND ")
0992:             *
0993:             * @throws SQLException sql related exception
0994:             * @throws ClassNotFoundException driver class not found
0995:             */
0996:            public void renderConstraints(StringBuffer q, INode home,
0997:                    INode nonvirtual, DbMapping otherDbm, String prefix)
0998:                    throws SQLException, ClassNotFoundException {
0999:
1000:                if (constraints.length > 1 && logicalOperator != AND) {
1001:                    q.append(prefix);
1002:                    q.append("(");
1003:                    prefix = "";
1004:                }
1005:
1006:                for (int i = 0; i < constraints.length; i++) {
1007:                    if (constraints[i].foreignKeyIsPrototype()) {
1008:                        // if foreign key is $prototype we already have this constraint
1009:                        // covered by doing the select on the proper table
1010:                        continue;
1011:                    }
1012:                    q.append(prefix);
1013:                    constraints[i].addToQuery(q, home, nonvirtual, otherDbm);
1014:                    prefix = logicalOperator;
1015:                }
1016:
1017:                if (constraints.length > 1 && logicalOperator != AND) {
1018:                    q.append(")");
1019:                    prefix = " AND ";
1020:                }
1021:
1022:                // also take the prototype into consideration if someone
1023:                // specifies an extension of an prototype inside the brakets of
1024:                // a type.properties's collection, only nodes having this proto
1025:                // sould appear inside the collection
1026:                if (otherDbm.inheritsStorage()) {
1027:                    String protoField = otherDbm.getPrototypeField();
1028:                    String[] extensions = otherDbm.getExtensions();
1029:
1030:                    // extensions should never be null for extension- and
1031:                    // extended prototypes. nevertheless we check it here
1032:                    if (extensions != null && protoField != null) {
1033:                        q.append(prefix);
1034:                        otherDbm.appendCondition(q, protoField, extensions);
1035:                        prefix = " AND ";
1036:                    }
1037:                }
1038:
1039:                if (filter != null) {
1040:                    appendFilter(q, nonvirtual, prefix);
1041:                }
1042:            }
1043:
1044:            /**
1045:             *  Render the constraints for this relation for use within
1046:             *  a left outer join select statement for the base object.
1047:             *
1048:             * @param select the string buffer to write to
1049:             * @param isOracle create Oracle pre-9 style left outer join
1050:             */
1051:            public void renderJoinConstraints(StringBuffer select,
1052:                    boolean isOracle) {
1053:                for (int i = 0; i < constraints.length; i++) {
1054:                    select.append(ownType.getTableName());
1055:                    select.append(".");
1056:                    select.append(constraints[i].localKey);
1057:                    select.append(" = ");
1058:                    select.append(JOIN_PREFIX);
1059:                    select.append(propName);
1060:                    select.append(".");
1061:                    select.append(constraints[i].foreignKey);
1062:                    if (isOracle) {
1063:                        // create old oracle style join - see
1064:                        // http://www.praetoriate.com/oracle_tips_outer_joins.htm
1065:                        select.append("(+)");
1066:                    }
1067:                    if (i == constraints.length - 1) {
1068:                        select.append(" ");
1069:                    } else {
1070:                        select.append(" AND ");
1071:                    }
1072:                }
1073:
1074:            }
1075:
1076:            /**
1077:             * Get the order section to use for this relation
1078:             */
1079:            public String getOrder() {
1080:                if (groupby != null) {
1081:                    return groupbyOrder;
1082:                } else {
1083:                    return order;
1084:                }
1085:            }
1086:
1087:            /**
1088:             *  Tell wether the property described by this relation is to be handled
1089:             *  as readonly/write protected.
1090:             */
1091:            public boolean isReadonly() {
1092:                return readonly;
1093:            }
1094:
1095:            /**
1096:             * Check if the child node fullfills the constraints defined by this relation.
1097:             * FIXME: This always returns false if the relation has a filter value set,
1098:             * since we can't determine if the filter constraints are met without
1099:             * querying the database.
1100:             *
1101:             * @param parent the parent object - may be a virtual or group node
1102:             * @param child the child object
1103:             * @return true if all constraints are met
1104:             */
1105:            public boolean checkConstraints(Node parent, Node child) {
1106:                // problem: if a filter property is defined for this relation,
1107:                // i.e. a piece of static SQL-where clause, we'd have to evaluate it
1108:                // in order to check the constraints. Because of this, if a filter
1109:                // is defined, we return false as soon as the modified-time is greater
1110:                // than the create-time of the child, i.e. if the child node has been
1111:                // modified since it was first fetched from the db.
1112:                if (filter != null && child.lastModified() > child.created()) {
1113:                    return false;
1114:                }
1115:
1116:                // counter for constraints and satisfied constraints
1117:                int count = 0;
1118:                int satisfied = 0;
1119:
1120:                INode nonvirtual = parent.getNonVirtualParent();
1121:                DbMapping otherDbm = child.getDbMapping();
1122:                if (otherDbm == null) {
1123:                    otherDbm = otherType;
1124:                }
1125:
1126:                for (int i = 0; i < constraints.length; i++) {
1127:                    Constraint cnst = constraints[i];
1128:                    String propname = cnst.foreignProperty(otherDbm);
1129:
1130:                    if (propname != null) {
1131:                        INode home = cnst.isGroupby ? parent : nonvirtual;
1132:                        String value = null;
1133:
1134:                        if (cnst.localKeyIsPrimary(home.getDbMapping())) {
1135:                            value = home.getID();
1136:                        } else if (cnst.localKeyIsPrototype()) {
1137:                            value = home.getDbMapping().getStorageTypeName();
1138:                        } else if (ownType.isRelational()) {
1139:                            value = home.getString(cnst.localProperty());
1140:                        } else {
1141:                            value = home.getString(cnst.localKey);
1142:                        }
1143:
1144:                        count++;
1145:
1146:                        if (value != null
1147:                                && value.equals(child.getString(propname))) {
1148:                            satisfied++;
1149:                        }
1150:                    }
1151:                }
1152:
1153:                // check if enough constraints are met depending on logical operator
1154:                if (logicalOperator == OR) {
1155:                    return satisfied > 0;
1156:                } else if (logicalOperator == XOR) {
1157:                    return satisfied == 1;
1158:                } else {
1159:                    return satisfied == count;
1160:                }
1161:            }
1162:
1163:            /**
1164:             * Make sure that the child node fullfills the constraints defined by this relation by setting the
1165:             * appropriate properties
1166:             */
1167:            public void setConstraints(Node parent, Node child) {
1168:
1169:                // if logical operator is OR or XOR we just return because we
1170:                // wouldn't know what to do anyway
1171:                if (logicalOperator != AND) {
1172:                    return;
1173:                }
1174:
1175:                Node home = parent.getNonVirtualParent();
1176:
1177:                for (int i = 0; i < constraints.length; i++) {
1178:                    Constraint cnst = constraints[i];
1179:                    // don't set groupby constraints since we don't know if the
1180:                    // parent node is the base node or a group node
1181:                    if (cnst.isGroupby) {
1182:                        continue;
1183:                    }
1184:
1185:                    // check if we update the local or the other object, depending on
1186:                    // whether the primary key of either side is used.
1187:                    boolean foreignIsPrimary = cnst.foreignKeyIsPrimary();
1188:                    if (foreignIsPrimary || cnst.foreignKeyIsPrototype()) {
1189:                        String localProp = cnst.localProperty();
1190:                        if (localProp == null) {
1191:                            ownType.app
1192:                                    .logError("Error: column "
1193:                                            + cnst.localKey
1194:                                            + " must be mapped in order to be used as constraint in "
1195:                                            + Relation.this );
1196:                        } else {
1197:                            String value = foreignIsPrimary ? child.getID()
1198:                                    : child.getDbMapping().getStorageTypeName();
1199:                            home.setString(localProp, value);
1200:                        }
1201:                        continue;
1202:                    }
1203:
1204:                    DbMapping otherDbm = child.getDbMapping();
1205:                    if (otherDbm == null) {
1206:                        otherDbm = otherType;
1207:                    }
1208:
1209:                    Relation crel = otherDbm
1210:                            .columnNameToRelation(cnst.foreignKey);
1211:
1212:                    if (crel != null) {
1213:
1214:                        if (cnst.localKeyIsPrimary(home.getDbMapping())) {
1215:                            // only set node if property in child object is defined as reference.
1216:                            if (crel.reftype == REFERENCE) {
1217:                                INode currentValue = child
1218:                                        .getNode(crel.propName);
1219:
1220:                                // we set the backwards reference iff the reference is currently unset, if
1221:                                // is set to a transient object, or if the new target is not transient. This
1222:                                // prevents us from overwriting a persistent refererence with a transient one,
1223:                                // which would most probably not be what we want.
1224:                                if ((currentValue == null)
1225:                                        || ((currentValue != home) && ((currentValue
1226:                                                .getState() == Node.TRANSIENT) || (home
1227:                                                .getState() != Node.TRANSIENT))))
1228:                                    try {
1229:                                        child.setNode(crel.propName, home);
1230:                                    } catch (Exception ignore) {
1231:                                        // in some cases, getNonVirtualParent() doesn't work
1232:                                        // correctly for transient nodes, so this may fail.
1233:                                    }
1234:                            } else if (crel.reftype == PRIMITIVE) {
1235:                                child.setString(crel.propName, home.getID());
1236:                            }
1237:                        } else if (crel.reftype == PRIMITIVE) {
1238:                            if (cnst.localKeyIsPrototype()) {
1239:                                child.setString(crel.propName, home
1240:                                        .getDbMapping().getStorageTypeName());
1241:                            } else {
1242:                                Property prop = home.getProperty(cnst
1243:                                        .localProperty());
1244:                                if (prop != null) {
1245:                                    child.set(crel.propName, prop.getValue(),
1246:                                            prop.getType());
1247:                                } else {
1248:                                    prop = child.getProperty(cnst
1249:                                            .foreignProperty(child
1250:                                                    .getDbMapping()));
1251:                                    if (prop != null) {
1252:                                        home.set(cnst.localProperty(), prop
1253:                                                .getValue(), prop.getType());
1254:                                    }
1255:                                }
1256:                            }
1257:                        }
1258:                    }
1259:                }
1260:            }
1261:
1262:            /**
1263:             * Unset the constraints that link two objects together.
1264:             */
1265:            public void unsetConstraints(Node parent, INode child) {
1266:                Node home = parent.getNonVirtualParent();
1267:
1268:                for (int i = 0; i < constraints.length; i++) {
1269:                    Constraint cnst = constraints[i];
1270:                    // don't set groupby constraints since we don't know if the
1271:                    // parent node is the base node or a group node
1272:                    if (cnst.isGroupby) {
1273:                        continue;
1274:                    }
1275:
1276:                    // check if we update the local or the other object, depending on
1277:                    // whether the primary key of either side is used.
1278:
1279:                    if (cnst.foreignKeyIsPrimary()
1280:                            || cnst.foreignKeyIsPrototype()) {
1281:                        String localProp = cnst.localProperty();
1282:                        if (localProp != null) {
1283:                            home.setString(localProp, null);
1284:                        }
1285:                        continue;
1286:                    }
1287:
1288:                    DbMapping otherDbm = child.getDbMapping();
1289:                    if (otherDbm == null) {
1290:                        otherDbm = otherType;
1291:                    }
1292:
1293:                    Relation crel = otherDbm
1294:                            .columnNameToRelation(cnst.foreignKey);
1295:
1296:                    if (crel != null) {
1297:                        if (cnst.localKeyIsPrimary(home.getDbMapping())) {
1298:                            // only set node if property in child object is defined as reference.
1299:                            if (crel.reftype == REFERENCE) {
1300:                                INode currentValue = child
1301:                                        .getNode(crel.propName);
1302:
1303:                                if ((currentValue == home)) {
1304:                                    child.setString(crel.propName, null);
1305:                                }
1306:                            } else if (crel.reftype == PRIMITIVE) {
1307:                                child.setString(crel.propName, null);
1308:                            }
1309:                        } else if (crel.reftype == PRIMITIVE) {
1310:                            child.setString(crel.propName, null);
1311:                        }
1312:                    }
1313:                }
1314:            }
1315:
1316:            /**
1317:             *  Returns a map containing the key/value pairs for a specific Node
1318:             */
1319:            public Map getKeyParts(INode home) {
1320:                Map map = new HashMap();
1321:                for (int i = 0; i < constraints.length; i++) {
1322:                    Constraint cnst = constraints[i];
1323:                    if (cnst.localKeyIsPrimary(ownType)) {
1324:                        map.put(cnst.foreignKey, home.getID());
1325:                    } else if (cnst.localKeyIsPrototype()) {
1326:                        map.put(cnst.foreignKey, home.getDbMapping()
1327:                                .getStorageTypeName());
1328:                    } else {
1329:                        map.put(cnst.foreignKey, home.getString(cnst
1330:                                .localProperty()));
1331:                    }
1332:                }
1333:                // add filter as pseudo-constraint
1334:                if (filter != null) {
1335:                    map.put("__filter__", filter);
1336:                }
1337:                return map;
1338:            }
1339:
1340:            /**
1341:             *
1342:             *
1343:             * @return ...
1344:             */
1345:            public String toString() {
1346:                String c = "";
1347:                String spacer = "";
1348:
1349:                if (constraints != null) {
1350:                    c = " constraints: ";
1351:                    for (int i = 0; i < constraints.length; i++) {
1352:                        c += spacer;
1353:                        c += constraints[i].toString();
1354:                        spacer = ", ";
1355:                    }
1356:                }
1357:
1358:                String target = otherType == null ? columnName : otherType
1359:                        .toString();
1360:
1361:                return "Relation " + ownType + "." + propName + " -> " + target
1362:                        + c;
1363:            }
1364:
1365:            /**
1366:             * The Constraint class represents a part of the where clause in the query used to
1367:             * establish a relation between database mapped objects.
1368:             */
1369:            class Constraint {
1370:                String localKey;
1371:                String foreignKey;
1372:                boolean isGroupby;
1373:
1374:                Constraint(String local, String foreign, boolean groupby) {
1375:                    localKey = local;
1376:                    foreignKey = foreign;
1377:                    isGroupby = groupby;
1378:                }
1379:
1380:                public void addToQuery(StringBuffer q, INode home,
1381:                        INode nonvirtual, DbMapping otherDbm)
1382:                        throws SQLException, ClassNotFoundException {
1383:                    String local;
1384:                    INode ref = isGroupby ? home : nonvirtual;
1385:
1386:                    if (localKeyIsPrimary(ref.getDbMapping())) {
1387:                        local = ref.getID();
1388:                    } else if (localKeyIsPrototype()) {
1389:                        local = ref.getDbMapping().getStorageTypeName();
1390:                    } else {
1391:                        String homeprop = ownType
1392:                                .columnNameToProperty(localKey);
1393:                        if (homeprop == null) {
1394:                            throw new SQLException("Invalid local name '"
1395:                                    + localKey + "' on " + ownType);
1396:                        }
1397:                        local = ref.getString(homeprop);
1398:                    }
1399:
1400:                    String columnName;
1401:                    if (foreignKeyIsPrimary()) {
1402:                        columnName = otherDbm.getIDField();
1403:                    } else {
1404:                        columnName = foreignKey;
1405:                    }
1406:                    otherDbm.appendCondition(q, columnName, local);
1407:                }
1408:
1409:                public boolean foreignKeyIsPrimary() {
1410:                    return (foreignKey == null)
1411:                            || "$id".equalsIgnoreCase(foreignKey)
1412:                            || foreignKey.equalsIgnoreCase(otherType
1413:                                    .getIDField());
1414:                }
1415:
1416:                public boolean foreignKeyIsPrototype() {
1417:                    return "$prototype".equalsIgnoreCase(foreignKey);
1418:                }
1419:
1420:                public boolean localKeyIsPrimary(DbMapping homeMapping) {
1421:                    return (homeMapping == null)
1422:                            || (localKey == null)
1423:                            || "$id".equalsIgnoreCase(localKey)
1424:                            || localKey.equalsIgnoreCase(homeMapping
1425:                                    .getIDField());
1426:                }
1427:
1428:                public boolean localKeyIsPrototype() {
1429:                    return "$prototype".equalsIgnoreCase(localKey);
1430:                }
1431:
1432:                public String foreignProperty(DbMapping otherDbm) {
1433:                    if (otherDbm.isRelational())
1434:                        return otherDbm.columnNameToProperty(foreignKey);
1435:                    return foreignKey;
1436:                }
1437:
1438:                public String localProperty() {
1439:                    if (ownType.isRelational())
1440:                        return ownType.columnNameToProperty(localKey);
1441:                    return localKey;
1442:                }
1443:
1444:                public String toString() {
1445:                    return localKey + "=" + otherType.getTypeName() + "."
1446:                            + foreignKey;
1447:                }
1448:            }
1449:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.