Source Code Cross Referenced for DbMapping.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: 8674 $
0014:         * $Date: 2007-11-28 16:32:09 +0100 (Mit, 28 Nov 2007) $
0015:         */
0016:
0017:        package helma.objectmodel.db;
0018:
0019:        import helma.framework.core.Application;
0020:        import helma.framework.core.Prototype;
0021:        import helma.util.ResourceProperties;
0022:
0023:        import java.sql.*;
0024:        import java.util.*;
0025:
0026:        /**
0027:         * A DbMapping describes how a certain type of  Nodes is to mapped to a
0028:         * relational database table. Basically it consists of a set of JavaScript property-to-
0029:         * Database row bindings which are represented by instances of the Relation class.
0030:         */
0031:        public final class DbMapping {
0032:            // DbMappings belong to an application
0033:            protected final Application app;
0034:
0035:            // prototype name of this mapping
0036:            private final String typename;
0037:
0038:            // properties from where the mapping is read
0039:            private final ResourceProperties props;
0040:
0041:            // name of data dbSource to which this mapping writes
0042:            private DbSource dbSource;
0043:
0044:            // name of datasource
0045:            private String dbSourceName;
0046:
0047:            // name of db table
0048:            private String tableName;
0049:
0050:            // the verbatim, unparsed _parent specification
0051:            private String parentSetting;
0052:
0053:            // list of properties to try for parent
0054:            private ParentInfo[] parentInfo;
0055:
0056:            // Relations describing subnodes and properties.
0057:            protected Relation subRelation;
0058:            protected Relation propRelation;
0059:
0060:            // if this defines a subnode mapping with groupby layer,
0061:            // we need a DbMapping for those groupby nodes
0062:            private DbMapping groupbyMapping;
0063:
0064:            // Map of property names to Relations objects
0065:            private HashMap prop2db;
0066:
0067:            // Map of db columns to Relations objects.
0068:            // Case insensitive, keys are stored in lower case so
0069:            // lookups must do a toLowerCase().
0070:            private HashMap db2prop;
0071:
0072:            // list of columns to fetch from db
0073:            private DbColumn[] columns = null;
0074:
0075:            // Map of db columns by name
0076:            private HashMap columnMap;
0077:
0078:            // Array of aggressively loaded references
0079:            private Relation[] joins;
0080:
0081:            // pre-rendered select statement
0082:            private String selectString = null;
0083:            private String insertString = null;
0084:            private String updateString = null;
0085:
0086:            // db field used as primary key
0087:            private String idField;
0088:
0089:            // db field used as object name
0090:            private String nameField;
0091:
0092:            // db field used to identify name of prototype to use for object instantiation
0093:            private String protoField;
0094:
0095:            // Used to map prototype ids to prototype names for
0096:            // prototypes which extend the prototype represented by
0097:            // this DbMapping.
0098:            private ResourceProperties extensionMap;
0099:
0100:            // a numeric or literal id used to represent this type in db
0101:            private String extensionId;
0102:
0103:            // dbmapping of parent prototype, if any
0104:            private DbMapping parentMapping;
0105:
0106:            // descriptor for key generation method
0107:            private String idgen;
0108:
0109:            // remember last key generated for this table
0110:            private long lastID;
0111:
0112:            // timestamp of last modification of the mapping (type.properties)
0113:            // init value is -1 so we know we have to run update once even if
0114:            // the underlying properties file is non-existent
0115:            long lastTypeChange = -1;
0116:
0117:            // timestamp of last modification of an object of this type
0118:            long lastDataChange = 0;
0119:
0120:            // evict objects of this type when received via replication
0121:            private boolean evictOnReplication;
0122:
0123:            // Set of mappings that depend on us and should be forwarded last data change events
0124:            HashSet dependentMappings = new HashSet();
0125:
0126:            // does this DbMapping describe a virtual node (collection, mountpoint, groupnode)?
0127:            private boolean virtual = false;
0128:
0129:            /**
0130:             * Create an internal DbMapping used for "virtual" mappings aka collections, mountpoints etc.
0131:             */
0132:            public DbMapping(Application app, String parentTypeName) {
0133:                this (app, parentTypeName, null);
0134:                // DbMappings created with this constructor always define virtual nodes
0135:                virtual = true;
0136:                if (parentTypeName != null) {
0137:                    parentMapping = app.getDbMapping(parentTypeName);
0138:                    if (parentMapping == null) {
0139:                        throw new IllegalArgumentException(
0140:                                "Unknown parent mapping: " + parentTypeName);
0141:                    }
0142:                }
0143:            }
0144:
0145:            /**
0146:             * Create a DbMapping from a type.properties property file
0147:             */
0148:            public DbMapping(Application app, String typename,
0149:                    ResourceProperties props) {
0150:                this .app = app;
0151:                // create a unique instance of the string. This is useful so
0152:                // we can compare types just by using == instead of equals.
0153:                this .typename = typename == null ? null : typename.intern();
0154:
0155:                prop2db = new HashMap();
0156:                db2prop = new HashMap();
0157:                columnMap = new HashMap();
0158:                parentInfo = null;
0159:                idField = null;
0160:                this .props = props;
0161:
0162:                if (props != null) {
0163:                    readBasicProperties();
0164:                }
0165:            }
0166:
0167:            /**
0168:             * Tell the type manager whether we need update() to be called
0169:             */
0170:            public boolean needsUpdate() {
0171:                return props.lastModified() != lastTypeChange;
0172:            }
0173:
0174:            /**
0175:             * Read in basic properties and register dbmapping with the
0176:             * dbsource.
0177:             */
0178:            private void readBasicProperties() {
0179:                tableName = props.getProperty("_table");
0180:                dbSourceName = props.getProperty("_db");
0181:
0182:                if (dbSourceName != null) {
0183:                    dbSource = app.getDbSource(dbSourceName);
0184:
0185:                    if (dbSource == null) {
0186:                        app
0187:                                .logError("*** Data Source for prototype "
0188:                                        + typename + " does not exist: "
0189:                                        + dbSourceName);
0190:                        app.logError("*** accessing or storing a " + typename
0191:                                + " object will cause an error.");
0192:                    } else if (tableName == null) {
0193:                        app
0194:                                .logError("*** No table name specified for prototype "
0195:                                        + typename);
0196:                        app.logError("*** accessing or storing a " + typename
0197:                                + " object will cause an error.");
0198:
0199:                        // mark mapping as invalid by nulling the dbSource field
0200:                        dbSource = null;
0201:                    } else {
0202:                        // dbSource and tableName not null - register this instance
0203:                        dbSource.registerDbMapping(this );
0204:                    }
0205:                }
0206:            }
0207:
0208:            /**
0209:             * Read the mapping from the Properties. Return true if the properties were changed.
0210:             * The read is split in two, this method and the rewire method. The reason is that in order
0211:             * for rewire to work, all other db mappings must have been initialized and registered.
0212:             */
0213:            public synchronized void update() {
0214:                // read in properties
0215:                readBasicProperties();
0216:                idgen = props.getProperty("_idgen");
0217:                // if id field is null, we assume "ID" as default. We don't set it
0218:                // however, so that if null we check the parent prototype first.
0219:                idField = props.getProperty("_id");
0220:                nameField = props.getProperty("_name");
0221:                protoField = props.getProperty("_prototype");
0222:                evictOnReplication = "true".equals(props
0223:                        .getProperty("_evictOnReplication"));
0224:
0225:                parentSetting = props.getProperty("_parent");
0226:                if (parentSetting != null) {
0227:                    // comma-separated list of properties to be used as parent
0228:                    StringTokenizer st = new StringTokenizer(parentSetting,
0229:                            ",;");
0230:                    parentInfo = new ParentInfo[st.countTokens()];
0231:
0232:                    for (int i = 0; i < parentInfo.length; i++) {
0233:                        parentInfo[i] = new ParentInfo(st.nextToken().trim());
0234:                    }
0235:                } else {
0236:                    parentInfo = null;
0237:                }
0238:
0239:                lastTypeChange = props.lastModified();
0240:
0241:                // see if this prototype extends (inherits from) any other prototype
0242:                String extendsProto = props.getProperty("_extends");
0243:
0244:                if (extendsProto != null) {
0245:                    parentMapping = app.getDbMapping(extendsProto);
0246:                    if (parentMapping == null) {
0247:                        app
0248:                                .logError("*** Parent mapping for prototype "
0249:                                        + typename + " does not exist: "
0250:                                        + extendsProto);
0251:                    } else {
0252:                        if (parentMapping.needsUpdate()) {
0253:                            parentMapping.update();
0254:                        }
0255:                        // if tableName or DbSource are inherited from the parent mapping
0256:                        // set them to null so we are aware of the fact.
0257:                        if (tableName != null
0258:                                && tableName.equals(parentMapping
0259:                                        .getTableName())) {
0260:                            tableName = null;
0261:                        }
0262:                        if (dbSourceName != null
0263:                                && dbSourceName.equals(parentMapping
0264:                                        .getDbSourceName())) {
0265:                            dbSourceName = null;
0266:                            dbSource = null;
0267:                        }
0268:                    }
0269:                } else {
0270:                    parentMapping = null;
0271:                }
0272:
0273:                if (inheritsStorage() && getPrototypeField() == null) {
0274:                    app
0275:                            .logError("*** Prototype not stored for extended relational type "
0276:                                    + typename);
0277:                    app
0278:                            .logError("*** objects fetched from db will have base prototype!");
0279:                }
0280:
0281:                // check if there is an extension-id specified inside the type.properties
0282:                extensionId = props.getProperty("_extensionId", typename);
0283:                registerExtension(extensionId, typename);
0284:
0285:                // set the parent prototype in the corresponding Prototype object!
0286:                // this was previously done by TypeManager, but we need to do it
0287:                // ourself because DbMapping.update() may be called by other code than
0288:                // the TypeManager.
0289:                if (typename != null && !"global".equalsIgnoreCase(typename)
0290:                        && !"hopobject".equalsIgnoreCase(typename)) {
0291:                    Prototype proto = app.getPrototypeByName(typename);
0292:                    if (proto != null) {
0293:                        if (extendsProto != null) {
0294:                            proto.setParentPrototype(app
0295:                                    .getPrototypeByName(extendsProto));
0296:                        } else if (!app.isJavaPrototype(typename)) {
0297:                            proto.setParentPrototype(app
0298:                                    .getPrototypeByName("hopobject"));
0299:                        }
0300:                    }
0301:                }
0302:
0303:                // null the cached columns and select string
0304:                columns = null;
0305:                columnMap.clear();
0306:                selectString = insertString = updateString = null;
0307:
0308:                HashMap p2d = new HashMap();
0309:                HashMap d2p = new HashMap();
0310:                ArrayList joinList = new ArrayList();
0311:
0312:                for (Enumeration e = props.keys(); e.hasMoreElements();) {
0313:                    String propName = (String) e.nextElement();
0314:
0315:                    try {
0316:                        // ignore internal properties (starting with "_") and sub-options (containing a ".")
0317:                        if (!propName.startsWith("_")
0318:                                && (propName.indexOf(".") < 0)) {
0319:                            String dbField = props.getProperty(propName);
0320:
0321:                            // check if a relation for this propery already exists. If so, reuse it
0322:                            Relation rel = (Relation) prop2db.get(propName
0323:                                    .toLowerCase());
0324:
0325:                            if (rel == null) {
0326:                                rel = new Relation(propName, this );
0327:                            }
0328:
0329:                            rel.update(dbField, props);
0330:
0331:                            // store relation with lower case property name
0332:                            // (ResourceProperties now preserve key capitalization!)
0333:                            p2d.put(propName.toLowerCase(), rel);
0334:
0335:                            if ((rel.columnName != null)
0336:                                    && rel.isPrimitiveOrReference()) {
0337:                                Relation old = (Relation) d2p.put(
0338:                                        rel.columnName.toLowerCase(), rel);
0339:                                // check if we're overwriting another relation
0340:                                // if so, primitive relations get precendence to references
0341:                                if (old != null) {
0342:                                    if (rel.isPrimitive() && old.isPrimitive()) {
0343:                                        app
0344:                                                .logEvent("*** Duplicate mapping for "
0345:                                                        + typename
0346:                                                        + "."
0347:                                                        + rel.columnName);
0348:                                    } else if (rel.isReference()
0349:                                            && old.isPrimitive()) {
0350:                                        // if a column is used both in a primitive and a reference mapping,
0351:                                        // use primitive mapping as primary one and mark reference as
0352:                                        // complex so it will be fetched separately
0353:                                        d2p.put(old.columnName.toLowerCase(),
0354:                                                old);
0355:                                        rel.reftype = Relation.COMPLEX_REFERENCE;
0356:                                    } else if (rel.isPrimitive()
0357:                                            && old.isReference()) {
0358:                                        old.reftype = Relation.COMPLEX_REFERENCE;
0359:                                    }
0360:                                }
0361:                            }
0362:
0363:                            // check if a reference is aggressively fetched
0364:                            if (rel.aggressiveLoading
0365:                                    && (rel.isReference() || rel
0366:                                            .isComplexReference())) {
0367:                                joinList.add(rel);
0368:                            }
0369:
0370:                            // app.logEvent ("Mapping "+propName+" -> "+dbField);
0371:                        }
0372:                    } catch (Exception x) {
0373:                        app.logEvent("Error in type.properties: "
0374:                                + x.getMessage());
0375:                    }
0376:                }
0377:
0378:                prop2db = p2d;
0379:                db2prop = d2p;
0380:
0381:                joins = new Relation[joinList.size()];
0382:                joins = (Relation[]) joinList.toArray(joins);
0383:
0384:                String subnodeMapping = props.getProperty("_children");
0385:
0386:                if (subnodeMapping != null) {
0387:                    try {
0388:                        // check if subnode relation already exists. If so, reuse it
0389:                        if (subRelation == null) {
0390:                            subRelation = new Relation("_children", this );
0391:                        }
0392:
0393:                        subRelation.update(subnodeMapping, props);
0394:
0395:                        // if subnodes are accessed via access name or group name,
0396:                        // the subnode relation is also the property relation.
0397:                        if ((subRelation.accessName != null)
0398:                                || (subRelation.groupby != null)) {
0399:                            propRelation = subRelation;
0400:                        } else {
0401:                            propRelation = null;
0402:                        }
0403:                    } catch (Exception x) {
0404:                        app.logEvent("Error reading _subnodes relation for "
0405:                                + typename + ": " + x.getMessage());
0406:
0407:                        // subRelation = null;
0408:                    }
0409:                } else {
0410:                    subRelation = propRelation = null;
0411:                }
0412:
0413:                if (groupbyMapping != null) {
0414:                    initGroupbyMapping();
0415:                    groupbyMapping.lastTypeChange = this .lastTypeChange;
0416:                }
0417:            }
0418:
0419:            /**
0420:             * Add the given extensionId and the coresponding prototypename
0421:             * to extensionMap for later lookup.
0422:             * @param extID the id mapping to the prototypename recogniced by helma
0423:             * @param extName the name of the extending prototype
0424:             */
0425:            private void registerExtension(String extID, String extName) {
0426:                // lazy initialization of extensionMap
0427:                if (extensionMap == null) {
0428:                    extensionMap = new ResourceProperties();
0429:                    extensionMap.setIgnoreCase(true);
0430:                } else if (extensionMap.containsValue(extName)) {
0431:                    // remove any preexisting mapping for the given childmapping
0432:                    extensionMap.values().remove(extName);
0433:                }
0434:                extensionMap.setProperty(extID, extName);
0435:                if (inheritsStorage()) {
0436:                    parentMapping.registerExtension(extID, extName);
0437:                }
0438:            }
0439:
0440:            /**
0441:             * Returns the Set of Prototypes extending this prototype
0442:             * @return the Set of Prototypes extending this prototype
0443:             */
0444:            public String[] getExtensions() {
0445:                return extensionMap == null ? new String[] { extensionId }
0446:                        : (String[]) extensionMap.keySet().toArray(
0447:                                new String[0]);
0448:            }
0449:
0450:            /**
0451:             * Looks up the prototype name identified by the given id, returing
0452:             * our own type name if it can't be resolved
0453:             * @param id the id specified for the prototype
0454:             * @return the name of the extending prototype
0455:             */
0456:            public String getPrototypeName(String id) {
0457:                if (inheritsStorage()) {
0458:                    return parentMapping.getPrototypeName(id);
0459:                }
0460:                // fallback to base-prototype if the proto isn't recogniced
0461:                if (id == null) {
0462:                    return typename;
0463:                }
0464:                return extensionMap.getProperty(id, typename);
0465:            }
0466:
0467:            /**
0468:             * get the id-value of this extension
0469:             */
0470:            public String getExtensionId() {
0471:                return extensionId;
0472:            }
0473:
0474:            /**
0475:             * Method in interface Updatable.
0476:             */
0477:            public void remove() {
0478:                // do nothing, removing of type properties is not implemented.
0479:            }
0480:
0481:            /**
0482:             * Get a JDBC connection for this DbMapping.
0483:             */
0484:            public Connection getConnection() throws ClassNotFoundException,
0485:                    SQLException {
0486:                if (dbSourceName == null) {
0487:                    if (parentMapping != null) {
0488:                        return parentMapping.getConnection();
0489:                    } else {
0490:                        throw new SQLException(
0491:                                "Tried to get Connection from non-relational embedded data source.");
0492:                    }
0493:                }
0494:
0495:                if (tableName == null) {
0496:                    throw new SQLException(
0497:                            "Invalid DbMapping, _table not specified: " + this );
0498:                }
0499:
0500:                // if dbSource was previously not available, check again
0501:                if (dbSource == null) {
0502:                    dbSource = app.getDbSource(dbSourceName);
0503:                }
0504:
0505:                if (dbSource == null) {
0506:                    throw new SQLException(
0507:                            "Datasource not defined or unable to load driver: "
0508:                                    + dbSourceName + ".");
0509:                }
0510:
0511:                return dbSource.getConnection();
0512:            }
0513:
0514:            /**
0515:             * Get the DbSource object for this DbMapping. The DbSource describes a JDBC
0516:             * data source including URL, JDBC driver, username and password.
0517:             */
0518:            public DbSource getDbSource() {
0519:                if (dbSource == null) {
0520:                    if ((tableName != null) && (dbSourceName != null)) {
0521:                        dbSource = app.getDbSource(dbSourceName);
0522:                    } else if (parentMapping != null) {
0523:                        return parentMapping.getDbSource();
0524:                    }
0525:                }
0526:
0527:                return dbSource;
0528:            }
0529:
0530:            /**
0531:             * Get the dbsource name used for this type mapping.
0532:             */
0533:            public String getDbSourceName() {
0534:                if ((dbSourceName == null) && (parentMapping != null)) {
0535:                    return parentMapping.getDbSourceName();
0536:                }
0537:
0538:                return dbSourceName;
0539:            }
0540:
0541:            /**
0542:             * Get the table name used for this type mapping.
0543:             */
0544:            public String getTableName() {
0545:                if ((tableName == null) && (parentMapping != null)) {
0546:                    return parentMapping.getTableName();
0547:                }
0548:
0549:                return tableName;
0550:            }
0551:
0552:            /**
0553:             * Get the application this DbMapping belongs to.
0554:             */
0555:            public Application getApplication() {
0556:                return app;
0557:            }
0558:
0559:            /**
0560:             * Get the name of this mapping's application
0561:             */
0562:            public String getAppName() {
0563:                return app.getName();
0564:            }
0565:
0566:            /**
0567:             * Get the name of the object type this DbMapping belongs to.
0568:             */
0569:            public String getTypeName() {
0570:                return typename;
0571:            }
0572:
0573:            /**
0574:             * Get the name of this type's parent type, if any.
0575:             */
0576:            public String getExtends() {
0577:                return parentMapping == null ? null : parentMapping
0578:                        .getTypeName();
0579:            }
0580:
0581:            /**
0582:             * Get the primary key column name for objects using this mapping.
0583:             */
0584:            public String getIDField() {
0585:                if ((idField == null) && (parentMapping != null)) {
0586:                    return parentMapping.getIDField();
0587:                }
0588:
0589:                return (idField == null) ? "ID" : idField;
0590:            }
0591:
0592:            /**
0593:             * Get the column used for (internal) names of objects of this type.
0594:             */
0595:            public String getNameField() {
0596:                if ((nameField == null) && (parentMapping != null)) {
0597:                    return parentMapping.getNameField();
0598:                }
0599:
0600:                return nameField;
0601:            }
0602:
0603:            /**
0604:             * Get the column used for names of prototype.
0605:             */
0606:            public String getPrototypeField() {
0607:                if ((protoField == null) && (parentMapping != null)) {
0608:                    return parentMapping.getPrototypeField();
0609:                }
0610:
0611:                return protoField;
0612:            }
0613:
0614:            /**
0615:             * Should objects of this type be evicted/discarded/reloaded when received via
0616:             * cache replication?
0617:             */
0618:            public boolean evictOnReplication() {
0619:                return evictOnReplication;
0620:            }
0621:
0622:            /**
0623:             * Translate a database column name to an object property name according to this mapping.
0624:             */
0625:            public String columnNameToProperty(String columnName) {
0626:                if (columnName == null) {
0627:                    return null;
0628:                }
0629:
0630:                // SEMIHACK: If columnName is a function call, try to extract actual
0631:                // column name from it
0632:                int open = columnName.indexOf('(');
0633:                int close = columnName.indexOf(')');
0634:                if (open > -1 && close > open) {
0635:                    columnName = columnName.substring(open + 1, close);
0636:                }
0637:
0638:                return _columnNameToProperty(columnName.toLowerCase());
0639:            }
0640:
0641:            private String _columnNameToProperty(final String columnName) {
0642:                Relation rel = (Relation) db2prop.get(columnName);
0643:
0644:                if ((rel == null) && (parentMapping != null)) {
0645:                    return parentMapping._columnNameToProperty(columnName);
0646:                }
0647:
0648:                if ((rel != null) && rel.isPrimitiveOrReference()) {
0649:                    return rel.propName;
0650:                }
0651:
0652:                return null;
0653:            }
0654:
0655:            /**
0656:             * Translate an object property name to a database column name according
0657:             * to this mapping. If no mapping is found, the property name is returned,
0658:             * assuming property and column names are equal.
0659:             */
0660:            public String propertyToColumnName(String propName) {
0661:                if (propName == null) {
0662:                    return null;
0663:                }
0664:
0665:                // prop2db stores keys in lower case
0666:                return _propertyToColumnName(propName.toLowerCase());
0667:            }
0668:
0669:            private String _propertyToColumnName(final String propName) {
0670:                Relation rel = (Relation) prop2db.get(propName);
0671:
0672:                if ((rel == null) && (parentMapping != null)) {
0673:                    return parentMapping._propertyToColumnName(propName);
0674:                }
0675:
0676:                if ((rel != null) && (rel.isPrimitiveOrReference())) {
0677:                    return rel.columnName;
0678:                }
0679:
0680:                return null;
0681:            }
0682:
0683:            /**
0684:             * Translate a database column name to an object property name according to this mapping.
0685:             */
0686:            public Relation columnNameToRelation(String columnName) {
0687:                if (columnName == null) {
0688:                    return null;
0689:                }
0690:
0691:                return _columnNameToRelation(columnName.toLowerCase());
0692:            }
0693:
0694:            private Relation _columnNameToRelation(final String columnName) {
0695:                Relation rel = (Relation) db2prop.get(columnName);
0696:
0697:                if ((rel == null) && (parentMapping != null)) {
0698:                    return parentMapping._columnNameToRelation(columnName);
0699:                }
0700:
0701:                return rel;
0702:            }
0703:
0704:            /**
0705:             * Translate an object property name to a database column name according to this mapping.
0706:             */
0707:            public Relation propertyToRelation(String propName) {
0708:                if (propName == null) {
0709:                    return null;
0710:                }
0711:
0712:                // FIXME: prop2db stores keys in lower case, because it gets them
0713:                // from a SystemProperties object which converts keys to lower case.
0714:                return _propertyToRelation(propName.toLowerCase());
0715:            }
0716:
0717:            private Relation _propertyToRelation(String propName) {
0718:                Relation rel = (Relation) prop2db.get(propName);
0719:
0720:                if ((rel == null) && (parentMapping != null)) {
0721:                    return parentMapping._propertyToRelation(propName);
0722:                }
0723:
0724:                return rel;
0725:            }
0726:
0727:            /**
0728:             * @return the parent info as unparsed string.
0729:             */
0730:            public String getParentSetting() {
0731:                if ((parentSetting == null) && (parentMapping != null)) {
0732:                    return parentMapping.getParentSetting();
0733:                }
0734:                return parentSetting;
0735:            }
0736:
0737:            /**
0738:             * @return the parent info array, which tells an object of this type how to
0739:             * determine its parent object.
0740:             */
0741:            public synchronized ParentInfo[] getParentInfo() {
0742:                if ((parentInfo == null) && (parentMapping != null)) {
0743:                    return parentMapping.getParentInfo();
0744:                }
0745:
0746:                return parentInfo;
0747:            }
0748:
0749:            /**
0750:             *
0751:             *
0752:             * @return ...
0753:             */
0754:            public DbMapping getSubnodeMapping() {
0755:                if (subRelation != null) {
0756:                    return subRelation.otherType;
0757:                }
0758:
0759:                if (parentMapping != null) {
0760:                    return parentMapping.getSubnodeMapping();
0761:                }
0762:
0763:                return null;
0764:            }
0765:
0766:            /**
0767:             *
0768:             *
0769:             * @param propname ...
0770:             *
0771:             * @return ...
0772:             */
0773:            public DbMapping getPropertyMapping(String propname) {
0774:                Relation rel = getPropertyRelation(propname);
0775:
0776:                if (rel != null) {
0777:                    // if this is a virtual node, it doesn't have a dbmapping
0778:                    if (rel.virtual && (rel.prototype == null)) {
0779:                        return null;
0780:                    } else {
0781:                        return rel.otherType;
0782:                    }
0783:                }
0784:
0785:                return null;
0786:            }
0787:
0788:            /**
0789:             * If subnodes are grouped by one of their properties, return the
0790:             * db-mapping with the right relations to create the group-by nodes
0791:             */
0792:            public synchronized DbMapping getGroupbyMapping() {
0793:                if ((subRelation == null) && (parentMapping != null)) {
0794:                    return parentMapping.getGroupbyMapping();
0795:                } else if (subRelation.groupby == null) {
0796:                    return null;
0797:                } else if (groupbyMapping == null) {
0798:                    initGroupbyMapping();
0799:                }
0800:
0801:                return groupbyMapping;
0802:            }
0803:
0804:            /**
0805:             * Initialize the dbmapping used for group-by nodes.
0806:             */
0807:            private void initGroupbyMapping() {
0808:                // if a prototype is defined for groupby nodes, use that
0809:                // if mapping doesn' exist or isn't defined, create a new (anonymous internal) one
0810:                groupbyMapping = new DbMapping(app,
0811:                        subRelation.groupbyPrototype);
0812:
0813:                // set subnode and property relations
0814:                groupbyMapping.subRelation = subRelation
0815:                        .getGroupbySubnodeRelation();
0816:
0817:                if (propRelation != null) {
0818:                    groupbyMapping.propRelation = propRelation
0819:                            .getGroupbyPropertyRelation();
0820:                } else {
0821:                    groupbyMapping.propRelation = subRelation
0822:                            .getGroupbyPropertyRelation();
0823:                }
0824:            }
0825:
0826:            /**
0827:             *
0828:             *
0829:             * @param rel ...
0830:             */
0831:            public void setPropertyRelation(Relation rel) {
0832:                propRelation = rel;
0833:            }
0834:
0835:            /**
0836:             *
0837:             *
0838:             * @return ...
0839:             */
0840:            public Relation getSubnodeRelation() {
0841:                if ((subRelation == null) && (parentMapping != null)) {
0842:                    return parentMapping.getSubnodeRelation();
0843:                }
0844:
0845:                return subRelation;
0846:            }
0847:
0848:            /**
0849:             * Return the list of defined property names as String array.
0850:             */
0851:            public String[] getPropertyNames() {
0852:                return (String[]) prop2db.keySet().toArray(
0853:                        new String[prop2db.size()]);
0854:            }
0855:
0856:            /**
0857:             *
0858:             *
0859:             * @return ...
0860:             */
0861:            private Relation getPropertyRelation() {
0862:                if ((propRelation == null) && (parentMapping != null)) {
0863:                    return parentMapping.getPropertyRelation();
0864:                }
0865:
0866:                return propRelation;
0867:            }
0868:
0869:            /**
0870:             *
0871:             *
0872:             * @param propname ...
0873:             *
0874:             * @return ...
0875:             */
0876:            public Relation getPropertyRelation(String propname) {
0877:                if (propname == null) {
0878:                    return getPropertyRelation();
0879:                }
0880:
0881:                // first try finding an exact match for the property name
0882:                Relation rel = getExactPropertyRelation(propname);
0883:
0884:                // if not defined, return the generic property mapping
0885:                if (rel == null) {
0886:                    rel = getPropertyRelation();
0887:                }
0888:
0889:                return rel;
0890:            }
0891:
0892:            /**
0893:             *
0894:             *
0895:             * @param propname ...
0896:             *
0897:             * @return ...
0898:             */
0899:            public Relation getExactPropertyRelation(String propname) {
0900:                if (propname == null) {
0901:                    return null;
0902:                }
0903:
0904:                Relation rel = (Relation) prop2db.get(propname.toLowerCase());
0905:
0906:                if ((rel == null) && (parentMapping != null)) {
0907:                    rel = parentMapping.getExactPropertyRelation(propname);
0908:                }
0909:
0910:                return rel;
0911:            }
0912:
0913:            /**
0914:             *
0915:             *
0916:             * @return ...
0917:             */
0918:            public String getSubnodeGroupby() {
0919:                if ((subRelation == null) && (parentMapping != null)) {
0920:                    return parentMapping.getSubnodeGroupby();
0921:                }
0922:
0923:                return (subRelation == null) ? null : subRelation.groupby;
0924:            }
0925:
0926:            /**
0927:             *
0928:             *
0929:             * @return ...
0930:             */
0931:            public String getIDgen() {
0932:                if ((idgen == null) && (parentMapping != null)) {
0933:                    return parentMapping.getIDgen();
0934:                }
0935:
0936:                return idgen;
0937:            }
0938:
0939:            /**
0940:             *
0941:             *
0942:             * @return ...
0943:             */
0944:            public WrappedNodeManager getWrappedNodeManager() {
0945:                if (app == null) {
0946:                    throw new RuntimeException(
0947:                            "Can't get node manager from internal db mapping");
0948:                }
0949:
0950:                return app.getWrappedNodeManager();
0951:            }
0952:
0953:            /**
0954:             *  Tell whether this data mapping maps to a relational database table. This returns true
0955:             *  if a datasource is specified, even if it is not a valid one. Otherwise, objects with invalid
0956:             *  mappings would be stored in the embedded db instead of an error being thrown, which is
0957:             *  not what we want.
0958:             */
0959:            public boolean isRelational() {
0960:                return dbSourceName != null
0961:                        || (parentMapping != null && parentMapping
0962:                                .isRelational());
0963:            }
0964:
0965:            /**
0966:             * Return an array of DbColumns for the relational table mapped by this DbMapping.
0967:             */
0968:            public synchronized DbColumn[] getColumns()
0969:                    throws ClassNotFoundException, SQLException {
0970:                if (!isRelational()) {
0971:                    throw new SQLException(
0972:                            "Can't get columns for non-relational data mapping "
0973:                                    + this );
0974:                }
0975:
0976:                // Use local variable cols to avoid synchronization (schema may be nulled elsewhere)
0977:                if (columns == null) {
0978:                    // we do two things here: set the SQL type on the Relation mappings
0979:                    // and build a string of column names.
0980:                    Connection con = getConnection();
0981:                    Statement stmt = con.createStatement();
0982:                    String table = getTableName();
0983:
0984:                    if (table == null) {
0985:                        throw new SQLException(
0986:                                "Table name is null in getColumns() for "
0987:                                        + this );
0988:                    }
0989:
0990:                    ResultSet rs = stmt.executeQuery(new StringBuffer(
0991:                            "SELECT * FROM ").append(table).append(
0992:                            " WHERE 1 = 0").toString());
0993:
0994:                    if (rs == null) {
0995:                        throw new SQLException("Error retrieving columns for "
0996:                                + this );
0997:                    }
0998:
0999:                    ResultSetMetaData meta = rs.getMetaData();
1000:
1001:                    // ok, we have the meta data, now loop through mapping...
1002:                    int ncols = meta.getColumnCount();
1003:                    ArrayList list = new ArrayList(ncols);
1004:
1005:                    for (int i = 0; i < ncols; i++) {
1006:                        String colName = meta.getColumnName(i + 1);
1007:                        Relation rel = columnNameToRelation(colName);
1008:
1009:                        DbColumn col = new DbColumn(colName, meta
1010:                                .getColumnType(i + 1), rel, this );
1011:                        list.add(col);
1012:                    }
1013:                    columns = (DbColumn[]) list.toArray(new DbColumn[list
1014:                            .size()]);
1015:                }
1016:
1017:                return columns;
1018:            }
1019:
1020:            /**
1021:             *  Return the array of relations that are fetched with objects of this type.
1022:             */
1023:            public Relation[] getJoins() {
1024:                return joins;
1025:            }
1026:
1027:            /**
1028:             *
1029:             *
1030:             * @param columnName ...
1031:             *
1032:             * @return ...
1033:             *
1034:             * @throws ClassNotFoundException ...
1035:             * @throws SQLException ...
1036:             */
1037:            public DbColumn getColumn(String columnName)
1038:                    throws ClassNotFoundException, SQLException {
1039:                DbColumn col = (DbColumn) columnMap.get(columnName);
1040:                if (col == null) {
1041:                    DbColumn[] cols = columns;
1042:                    if (cols == null) {
1043:                        cols = getColumns();
1044:                    }
1045:                    for (int i = 0; i < cols.length; i++) {
1046:                        if (columnName.equalsIgnoreCase(cols[i].getName())) {
1047:                            col = cols[i];
1048:                            break;
1049:                        }
1050:                    }
1051:                    columnMap.put(columnName, col);
1052:                }
1053:                return col;
1054:            }
1055:
1056:            /**
1057:             *  Get a StringBuffer initialized to the first part of the select statement
1058:             *  for objects defined by this DbMapping
1059:             *
1060:             * @param rel the Relation we use to select. Currently only used for optimizer hints.
1061:             *            Is null if selecting by primary key.
1062:             * @return the StringBuffer containing the first part of the select query
1063:             */
1064:            public StringBuffer getSelect(Relation rel) {
1065:                // assign to local variable first so we are thread safe
1066:                // (selectString may be reset by other threads)
1067:                String sel = selectString;
1068:                boolean isOracle = isOracle();
1069:
1070:                if (rel == null && sel != null) {
1071:                    return new StringBuffer(sel);
1072:                }
1073:
1074:                StringBuffer s = new StringBuffer("SELECT ");
1075:
1076:                if (rel != null && rel.queryHints != null) {
1077:                    s.append(rel.queryHints).append(" ");
1078:                }
1079:
1080:                String table = getTableName();
1081:
1082:                // all columns from the main table
1083:                s.append(table);
1084:                s.append(".*");
1085:
1086:                for (int i = 0; i < joins.length; i++) {
1087:                    if (!joins[i].otherType.isRelational()) {
1088:                        continue;
1089:                    }
1090:                    s.append(", ");
1091:                    s.append(Relation.JOIN_PREFIX);
1092:                    s.append(joins[i].propName);
1093:                    s.append(".*");
1094:                }
1095:
1096:                s.append(" FROM ");
1097:
1098:                s.append(table);
1099:
1100:                if (rel != null) {
1101:                    rel.appendAdditionalTables(s);
1102:                }
1103:
1104:                s.append(" ");
1105:
1106:                for (int i = 0; i < joins.length; i++) {
1107:                    if (!joins[i].otherType.isRelational()) {
1108:                        continue;
1109:                    }
1110:                    if (isOracle) {
1111:                        // generate an old-style oracle left join - see
1112:                        // http://www.praetoriate.com/oracle_tips_outer_joins.htm
1113:                        s.append(", ");
1114:                        s.append(joins[i].otherType.getTableName());
1115:                        s.append(" ");
1116:                        s.append(Relation.JOIN_PREFIX);
1117:                        s.append(joins[i].propName);
1118:                        s.append(" ");
1119:                    } else {
1120:                        s.append("LEFT OUTER JOIN ");
1121:                        s.append(joins[i].otherType.getTableName());
1122:                        s.append(" ");
1123:                        s.append(Relation.JOIN_PREFIX);
1124:                        s.append(joins[i].propName);
1125:                        s.append(" ON ");
1126:                        joins[i].renderJoinConstraints(s, isOracle);
1127:                    }
1128:                }
1129:
1130:                // cache rendered string for later calls, but only if it wasn't
1131:                // built for a particular Relation
1132:                if (rel == null) {
1133:                    selectString = s.toString();
1134:                }
1135:
1136:                return s;
1137:            }
1138:
1139:            /**
1140:             *
1141:             *
1142:             * @return ...
1143:             */
1144:            public String getInsert() throws ClassNotFoundException,
1145:                    SQLException {
1146:                String ins = insertString;
1147:
1148:                if (ins != null) {
1149:                    return ins;
1150:                }
1151:
1152:                StringBuffer b1 = new StringBuffer("INSERT INTO ");
1153:                StringBuffer b2 = new StringBuffer(" ) VALUES ( ");
1154:                b1.append(getTableName());
1155:                b1.append(" ( ");
1156:
1157:                DbColumn[] cols = getColumns();
1158:                boolean needsComma = false;
1159:
1160:                for (int i = 0; i < cols.length; i++) {
1161:                    if (cols[i].isMapped()) {
1162:                        if (needsComma) {
1163:                            b1.append(", ");
1164:                            b2.append(", ");
1165:                        }
1166:                        b1.append(cols[i].getName());
1167:                        b2.append("?");
1168:                        needsComma = true;
1169:                    }
1170:                }
1171:
1172:                b1.append(b2.toString());
1173:                b1.append(" )");
1174:
1175:                // cache rendered string for later calls.
1176:                ins = insertString = b1.toString();
1177:
1178:                return ins;
1179:            }
1180:
1181:            /**
1182:             *
1183:             *
1184:             * @return ...
1185:             */
1186:            public StringBuffer getUpdate() {
1187:                String upd = updateString;
1188:
1189:                if (upd != null) {
1190:                    return new StringBuffer(upd);
1191:                }
1192:
1193:                StringBuffer s = new StringBuffer("UPDATE ");
1194:
1195:                s.append(getTableName());
1196:                s.append(" SET ");
1197:
1198:                // cache rendered string for later calls.
1199:                updateString = s.toString();
1200:
1201:                return s;
1202:            }
1203:
1204:            /**
1205:             *  Return true if values for the column identified by the parameter need
1206:             *  to be quoted in SQL queries.
1207:             */
1208:            public boolean needsQuotes(String columnName) throws SQLException,
1209:                    ClassNotFoundException {
1210:                if ((tableName == null) && (parentMapping != null)) {
1211:                    return parentMapping.needsQuotes(columnName);
1212:                }
1213:                DbColumn col = getColumn(columnName);
1214:                // This is not a mapped column. In case of doubt, add quotes.
1215:                if (col == null) {
1216:                    return true;
1217:                } else {
1218:                    return col.needsQuotes();
1219:                }
1220:            }
1221:
1222:            /**
1223:             * Add constraints to select query string to join object references
1224:             */
1225:            public void addJoinConstraints(StringBuffer s, String pre) {
1226:                boolean isOracle = isOracle();
1227:                String prefix = pre;
1228:
1229:                if (!isOracle) {
1230:                    // constraints have already been rendered by getSelect()
1231:                    return;
1232:                }
1233:
1234:                for (int i = 0; i < joins.length; i++) {
1235:                    if (!joins[i].otherType.isRelational()) {
1236:                        continue;
1237:                    }
1238:                    s.append(prefix);
1239:                    joins[i].renderJoinConstraints(s, isOracle);
1240:                    prefix = " AND ";
1241:                }
1242:            }
1243:
1244:            /**
1245:             * Is the database behind this an Oracle db?
1246:             *
1247:             * @return true if the dbsource is using an oracle JDBC driver
1248:             */
1249:            public boolean isOracle() {
1250:                if (dbSource != null) {
1251:                    return dbSource.isOracle();
1252:                }
1253:                if (parentMapping != null) {
1254:                    return parentMapping.isOracle();
1255:                }
1256:                return false;
1257:            }
1258:
1259:            /**
1260:             * Is the database behind this a MySQL db?
1261:             *
1262:             * @return true if the dbsource is using a MySQL JDBC driver
1263:             */
1264:            public boolean isMySQL() {
1265:                if (dbSource != null) {
1266:                    return dbSource.isMySQL();
1267:                }
1268:                if (parentMapping != null) {
1269:                    return parentMapping.isMySQL();
1270:                }
1271:                return false;
1272:            }
1273:
1274:            /**
1275:             * Is the database behind this a PostgreSQL db?
1276:             *
1277:             * @return true if the dbsource is using a PostgreSQL JDBC driver
1278:             */
1279:            public boolean isPostgreSQL() {
1280:                if (dbSource != null) {
1281:                    return dbSource.isPostgreSQL();
1282:                }
1283:                if (parentMapping != null) {
1284:                    return parentMapping.isPostgreSQL();
1285:                }
1286:                return false;
1287:            }
1288:
1289:            /**
1290:             * Is the database behind this a H2 db?
1291:             *
1292:             * @return true if the dbsource is using a H2 JDBC driver
1293:             */
1294:            public boolean isH2() {
1295:                if (dbSource != null) {
1296:                    return dbSource.isH2();
1297:                }
1298:                if (parentMapping != null) {
1299:                    return parentMapping.isH2();
1300:                }
1301:                return false;
1302:            }
1303:
1304:            /**
1305:             * Return a string representation for this DbMapping
1306:             *
1307:             * @return a string representation
1308:             */
1309:            public String toString() {
1310:                if (typename == null) {
1311:                    return "[unspecified internal DbMapping]";
1312:                } else {
1313:                    return ("[" + app.getName() + "." + typename + "]");
1314:                }
1315:            }
1316:
1317:            /**
1318:             * Get the last time something changed in the Mapping
1319:             *
1320:             * @return time of last mapping change
1321:             */
1322:            public long getLastTypeChange() {
1323:                return lastTypeChange;
1324:            }
1325:
1326:            /**
1327:             * Get the last time something changed in our data
1328:             *
1329:             * @return time of last data change
1330:             */
1331:            public long getLastDataChange() {
1332:                // refer to parent mapping if it uses the same db/table
1333:                if (inheritsStorage()) {
1334:                    return parentMapping.getLastDataChange();
1335:                } else {
1336:                    return lastDataChange;
1337:                }
1338:            }
1339:
1340:            /**
1341:             * Set the last time something changed in the data, propagating the event
1342:             * to mappings that depend on us through an additionalTables switch.
1343:             */
1344:            public void setLastDataChange() {
1345:                // forward data change timestamp to storage-compatible parent mapping
1346:                if (inheritsStorage()) {
1347:                    parentMapping.setLastDataChange();
1348:                } else {
1349:                    lastDataChange += 1;
1350:                    // propagate data change timestamp to mappings that depend on us
1351:                    if (!dependentMappings.isEmpty()) {
1352:                        Iterator it = dependentMappings.iterator();
1353:                        while (it.hasNext()) {
1354:                            DbMapping dbmap = (DbMapping) it.next();
1355:                            dbmap.setIndirectDataChange();
1356:                        }
1357:                    }
1358:                }
1359:            }
1360:
1361:            /**
1362:             * Set the last time something changed in the data. This is already an indirect
1363:             * data change triggered by a mapping we depend on, so we don't propagate it to
1364:             * mappings that depend on us through an additionalTables switch.
1365:             */
1366:            protected void setIndirectDataChange() {
1367:                // forward data change timestamp to storage-compatible parent mapping
1368:                if (inheritsStorage()) {
1369:                    parentMapping.setIndirectDataChange();
1370:                } else {
1371:                    lastDataChange += 1;
1372:                }
1373:            }
1374:
1375:            /**
1376:             * Helper method to generate a new ID. This is only used in the special case
1377:             * when using the select(max) method and the underlying table is still empty.
1378:             *
1379:             * @param dbmax the maximum value already stored in db
1380:             * @return a new and hopefully unique id
1381:             */
1382:            protected synchronized long getNewID(long dbmax) {
1383:                // refer to parent mapping if it uses the same db/table
1384:                if (inheritsStorage()) {
1385:                    return parentMapping.getNewID(dbmax);
1386:                } else {
1387:                    lastID = Math.max(dbmax + 1, lastID + 1);
1388:                    return lastID;
1389:                }
1390:            }
1391:
1392:            /**
1393:             * Return an enumeration of all properties defined by this db mapping.
1394:             *
1395:             * @return the property enumeration
1396:             */
1397:            public Enumeration getPropertyEnumeration() {
1398:                HashSet set = new HashSet();
1399:
1400:                collectPropertyNames(set);
1401:
1402:                final Iterator it = set.iterator();
1403:
1404:                return new Enumeration() {
1405:                    public boolean hasMoreElements() {
1406:                        return it.hasNext();
1407:                    }
1408:
1409:                    public Object nextElement() {
1410:                        return it.next();
1411:                    }
1412:                };
1413:            }
1414:
1415:            /**
1416:             * Collect a set of all properties defined by this db mapping
1417:             *
1418:             * @param basket the set to put properties into
1419:             */
1420:            private void collectPropertyNames(HashSet basket) {
1421:                // fetch propnames from parent mapping first, than add our own.
1422:                if (parentMapping != null) {
1423:                    parentMapping.collectPropertyNames(basket);
1424:                }
1425:
1426:                if (!prop2db.isEmpty()) {
1427:                    basket.addAll(prop2db.keySet());
1428:                }
1429:            }
1430:
1431:            /**
1432:             * Return the name of the prototype which specifies the storage location
1433:             * (dbsource + tablename) for this type, or null if it is stored in the embedded
1434:             * db.
1435:             */
1436:            public String getStorageTypeName() {
1437:                if (inheritsStorage()) {
1438:                    return parentMapping.getStorageTypeName();
1439:                }
1440:                return (dbSourceName == null) ? null : typename;
1441:            }
1442:
1443:            /**
1444:             * Check whether this DbMapping inherits its storage location from its
1445:             * parent mapping. The raison d'etre for this is that we need to detect
1446:             * inherited storage even if the dbsource and table are explicitly set
1447:             * in the extended mapping.
1448:             *
1449:             * @return true if this mapping shares its parent mapping storage
1450:             */
1451:            protected boolean inheritsStorage() {
1452:                // note: tableName and dbSourceName are nulled out in update() if they
1453:                // are inherited from the parent mapping. This way we know that
1454:                // storage is not inherited if either of them is not null.
1455:                return isRelational() && parentMapping != null
1456:                        && tableName == null && dbSourceName == null;
1457:            }
1458:
1459:            /**
1460:             * Static utility method to check whether two DbMappings use the same storage.
1461:             *
1462:             * @return true if both use the embedded database or the same relational table.
1463:             */
1464:            public static boolean areStorageCompatible(DbMapping dbm1,
1465:                    DbMapping dbm2) {
1466:                if (dbm1 == null)
1467:                    return dbm2 == null || !dbm2.isRelational();
1468:                return dbm1.isStorageCompatible(dbm2);
1469:            }
1470:
1471:            /**
1472:             * Tell if this DbMapping uses the same storage as the given DbMapping.
1473:             *
1474:             * @return true if both use the embedded database or the same relational table.
1475:             */
1476:            public boolean isStorageCompatible(DbMapping other) {
1477:                if (other == null) {
1478:                    return !isRelational();
1479:                } else if (other == this ) {
1480:                    return true;
1481:                } else if (isRelational()) {
1482:                    return getTableName().equals(other.getTableName())
1483:                            && getDbSource().equals(other.getDbSource());
1484:                }
1485:
1486:                return !other.isRelational();
1487:            }
1488:
1489:            /**
1490:             *  Return true if this db mapping represents the prototype indicated
1491:             *  by the string argument, either itself or via one of its parent prototypes.
1492:             */
1493:            public boolean isInstanceOf(String other) {
1494:                if ((typename != null) && typename.equals(other)) {
1495:                    return true;
1496:                }
1497:
1498:                DbMapping p = parentMapping;
1499:
1500:                while (p != null) {
1501:                    if ((p.typename != null) && p.typename.equals(other)) {
1502:                        return true;
1503:                    }
1504:
1505:                    p = p.parentMapping;
1506:                }
1507:
1508:                return false;
1509:            }
1510:
1511:            /**
1512:             * Get the mapping we inherit from, or null
1513:             *
1514:             * @return the parent DbMapping, or null
1515:             */
1516:            public DbMapping getParentMapping() {
1517:                return parentMapping;
1518:            }
1519:
1520:            /**
1521:             * Get our ResourceProperties
1522:             *
1523:             * @return our properties
1524:             */
1525:            public ResourceProperties getProperties() {
1526:                return props;
1527:            }
1528:
1529:            /**
1530:             * Register a DbMapping that depends on this DbMapping, so that collections of other mapping
1531:             * should be reloaded if data on this mapping is updated.
1532:             *
1533:             * @param dbmap the DbMapping that depends on us
1534:             */
1535:            protected void addDependency(DbMapping dbmap) {
1536:                this .dependentMappings.add(dbmap);
1537:            }
1538:
1539:            /**
1540:             * Append a sql-condition for the given column which must have
1541:             * one of the values contained inside the given Set to the given
1542:             * StringBuffer. 
1543:             * @param q the StringBuffer to append to
1544:             * @param column the column which must match one of the values
1545:             * @param values the list of values
1546:             * @throws SQLException
1547:             */
1548:            protected void appendCondition(StringBuffer q, String column,
1549:                    String[] values) throws SQLException,
1550:                    ClassNotFoundException {
1551:                if (values.length == 1) {
1552:                    appendCondition(q, column, values[0]);
1553:                    return;
1554:                }
1555:                if (column.indexOf('(') == -1 && column.indexOf('.') == -1) {
1556:                    q.append(getTableName()).append(".");
1557:                }
1558:                q.append(column).append(" in (");
1559:
1560:                if (needsQuotes(column)) {
1561:                    for (int i = 0; i < values.length; i++) {
1562:                        if (i > 0)
1563:                            q.append(", ");
1564:                        q.append("'").append(escapeString(values[i])).append(
1565:                                "'");
1566:                    }
1567:                } else {
1568:                    for (int i = 0; i < values.length; i++) {
1569:                        if (i > 0)
1570:                            q.append(", ");
1571:                        q.append(checkNumber(values[i]));
1572:                    }
1573:                }
1574:                q.append(")");
1575:            }
1576:
1577:            /**
1578:             * Append a sql-condition for the given column which must have
1579:             * the value given to the given StringBuffer. 
1580:             * @param q the StringBuffer to append to
1581:             * @param column the column which must match one of the values
1582:             * @param val the value
1583:             * @throws SQLException
1584:             */
1585:            protected void appendCondition(StringBuffer q, String column,
1586:                    String val) throws SQLException, ClassNotFoundException {
1587:                if (column.indexOf('(') == -1 && column.indexOf('.') == -1) {
1588:                    q.append(getTableName()).append(".");
1589:                }
1590:                q.append(column).append(" = ");
1591:
1592:                if (needsQuotes(column)) {
1593:                    q.append("'").append(escapeString(val)).append("'");
1594:                } else {
1595:                    q.append(checkNumber(val));
1596:                }
1597:            }
1598:
1599:            /**
1600:             * a utility method to escape single quotes used for inserting
1601:             * string-values into relational databases.
1602:             * Searches for "'" characters and escapes them by duplicating them (= "''")
1603:             * @param str the string to escape
1604:             * @return the escaped string
1605:             */
1606:            static String escapeString(Object value) {
1607:                String str = value == null ? null : value.toString();
1608:                if (str == null) {
1609:                    return null;
1610:                } else if (str.indexOf("'") < 0) {
1611:                    return str;
1612:                }
1613:
1614:                int l = str.length();
1615:                StringBuffer sbuf = new StringBuffer(l + 10);
1616:
1617:                for (int i = 0; i < l; i++) {
1618:                    char c = str.charAt(i);
1619:
1620:                    if (c == '\'') {
1621:                        sbuf.append('\'');
1622:                    }
1623:                    sbuf.append(c);
1624:                }
1625:                return sbuf.toString();
1626:            }
1627:
1628:            /**
1629:             * Utility method to check whether the argument is a number literal.
1630:             * @param str a string representing a number literal
1631:             * @return the argument, if it conforms to the number literal syntax
1632:             * @throws IllegalArgumentException if the argument does not represent a number
1633:             */
1634:            static String checkNumber(Object value)
1635:                    throws IllegalArgumentException {
1636:                String str = value == null ? null : value.toString();
1637:                if (str == null) {
1638:                    return null;
1639:                } else {
1640:                    str = str.trim();
1641:                    if (str.matches("(?:\\+|\\-)??\\d+(?:\\.\\d+)??")) {
1642:                        return str;
1643:                    }
1644:                }
1645:                throw new IllegalArgumentException("Illegal numeric literal: "
1646:                        + str);
1647:            }
1648:
1649:            /**
1650:             * Find if this DbMapping describes a virtual node (collection, mountpoint, groupnode)
1651:             * @return true if this instance describes a virtual node.
1652:             */
1653:            public boolean isVirtual() {
1654:                return virtual;
1655:            }
1656:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.