Changeset 9557

Show
Ignore:
Timestamp:
03/31/09 13:54:21 (1 year ago)
Author:
hannes
Message:

Add static getCollection() method on HopObject constructors to generate collections programmatically and on the fly. Implement limit and offset collection properties for databases that support it (Postgresql + Mysql)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • helma/helma/trunk/src/helma/objectmodel/db/DbMapping.java

    r9520 r9557  
    3737 
    3838    // properties from where the mapping is read 
    39     private final ResourceProperties props; 
     39    private final Properties props; 
    4040 
    4141    // name of data dbSource to which this mapping writes 
     
    148148     * Create a DbMapping from a type.properties property file 
    149149     */ 
    150     public DbMapping(Application app, String typename, ResourceProperties props) { 
     150    public DbMapping(Application app, String typename, Properties props) { 
    151151        this.app = app; 
    152152        // create a unique instance of the string. This is useful so 
     
    170170     */ 
    171171    public boolean needsUpdate() { 
    172         return props.lastModified() != lastTypeChange; 
     172        if (props instanceof ResourceProperties) { 
     173            return ((ResourceProperties) props).lastModified() != lastTypeChange; 
     174        } 
     175        return false; 
    173176    } 
    174177 
     
    234237        } 
    235238 
    236         lastTypeChange = props.lastModified(); 
     239        lastTypeChange = props instanceof ResourceProperties ? 
     240                ((ResourceProperties) props).lastModified() : System.currentTimeMillis(); 
    237241 
    238242        // see if this prototype extends (inherits from) any other prototype 
     
    299303        ArrayList joinList = new ArrayList(); 
    300304 
    301         for (Enumeration e = props.keys(); e.hasMoreElements();) { 
    302             String propName = (String) e.nextElement(); 
     305        for (Iterator it = props.entrySet().iterator(); it.hasNext(); ) { 
     306            Map.Entry entry =  (Map.Entry) it.next(); 
    303307 
    304308            try { 
     309                String propName = (String) entry.getKey(); 
     310 
    305311                // ignore internal properties (starting with "_") and sub-options (containing a ".") 
    306312                if (!propName.startsWith("_") && (propName.indexOf(".") < 0)) { 
    307                     String dbField = props.getProperty(propName); 
     313                    Object propValue = entry.getValue(); 
    308314 
    309315                    // check if a relation for this propery already exists. If so, reuse it 
     
    314320                    } 
    315321 
    316                     rel.update(dbField, props); 
     322                    rel.update(propValue, getSubProperties(propName)); 
    317323                    p2d.put(propName, rel); 
    318324 
     
    355361        joins = (Relation[]) joinList.toArray(joins); 
    356362 
    357         String subnodeMapping = props.getProperty("_children"); 
     363        Object subnodeMapping = props.get("_children"); 
    358364 
    359365        if (subnodeMapping != null) { 
     
    364370                } 
    365371 
    366                 subRelation.update(subnodeMapping, props); 
     372                subRelation.update(subnodeMapping, getSubProperties("_children")); 
    367373 
    368374                // if subnodes are accessed via access name or group name, 
     
    397403    private void registerExtension(String extID, String extName) { 
    398404        // lazy initialization of extensionMap 
     405        if (extID == null) { 
     406            return; 
     407        } 
    399408        if (extensionMap == null) { 
    400409            extensionMap = new ResourceProperties(); 
     
    14701479     * @return our properties 
    14711480     */ 
    1472     public ResourceProperties getProperties() { 
     1481    public Properties getProperties() { 
    14731482        return props; 
     1483    } 
     1484 
     1485    public Properties getSubProperties(String prefix) { 
     1486        if (props.get(prefix) instanceof Properties) { 
     1487            return (Properties) props.get(prefix); 
     1488        } else if (props instanceof ResourceProperties) { 
     1489            return ((ResourceProperties) props).getSubProperties(prefix + "."); 
     1490        } else { 
     1491            Properties subprops = new Properties(); 
     1492            prefix = prefix + "."; 
     1493            Iterator it = props.entrySet().iterator(); 
     1494            int prefixLength = prefix.length(); 
     1495            while (it.hasNext()) { 
     1496                Map.Entry entry = (Map.Entry) it.next(); 
     1497                String key = entry.getKey().toString(); 
     1498                if (key.regionMatches(false, 0, prefix, 0, prefixLength)) { 
     1499                    subprops.put(key.substring(prefixLength), entry.getValue()); 
     1500                } 
     1501            } 
     1502            return subprops; 
     1503        } 
    14741504    } 
    14751505 
  • helma/helma/trunk/src/helma/objectmodel/db/Relation.java

    r9519 r9557  
    103103    Vector filterPropertyRefs; 
    104104    int maxSize = 0; 
     105    int offset = 0; 
    105106 
    106107    /** 
     
    124125        this.queryHints =               rel.queryHints; 
    125126        this.maxSize =                  rel.maxSize; 
     127        this.offset =                   rel.offset; 
    126128        this.constraints =              rel.constraints; 
    127129        this.accessName =               rel.accessName; 
     
    145147    // parse methods for new file format 
    146148    //////////////////////////////////////////////////////////////////////////////////////////// 
    147     public void update(String desc, ResourceProperties props) { 
     149    public void update(Object desc, Properties props) { 
    148150        Application app = ownType.getApplication(); 
    149151 
    150         if ((desc == null) || "".equals(desc.trim())) { 
    151             if (propName != null) { 
    152                 reftype = PRIMITIVE; 
    153                 columnName = propName; 
     152        if (desc instanceof Properties || parseDescriptor(desc, props)) { 
     153            // new style foo.collectionOf = Bar mapping 
     154            String proto; 
     155            if (props.containsKey("collection")) { 
     156                proto = props.getProperty("collection"); 
     157                virtual = !"_children".equalsIgnoreCase(propName); 
     158                reftype = COLLECTION; 
     159            } else if (props.containsKey("mountpoint")) { 
     160                proto = props.getProperty("mountpoint"); 
     161                reftype = COLLECTION; 
     162                virtual = true; 
     163                this.prototype = proto; 
     164            } else if (props.containsKey("object")) { 
     165                proto = props.getProperty("object"); 
     166                if (reftype != COMPLEX_REFERENCE) { 
     167                    reftype = REFERENCE; 
     168                } 
     169                virtual = false; 
    154170            } else { 
    155                 reftype = INVALID; 
    156                 columnName = propName; 
    157             } 
    158         } else { 
    159             desc = desc.trim(); 
    160  
    161             int open = desc.indexOf("("); 
    162             int close = desc.indexOf(")"); 
    163  
    164             if ((open > -1) && (close > open)) { 
    165                 String ref = desc.substring(0, open).trim(); 
    166                 String proto = desc.substring(open + 1, close).trim(); 
    167  
    168                 if ("collection".equalsIgnoreCase(ref)) { 
    169                     virtual = !"_children".equalsIgnoreCase(propName); 
    170                     reftype = COLLECTION; 
    171                 } else if ("mountpoint".equalsIgnoreCase(ref)) { 
    172                     virtual = true; 
    173                     reftype = COLLECTION; 
    174                     prototype = proto; 
    175                 } else if ("object".equalsIgnoreCase(ref)) { 
    176                     virtual = false; 
    177                     if (reftype != COMPLEX_REFERENCE) { 
    178                         reftype = REFERENCE; 
    179                     } 
    180                 } else { 
    181                     throw new RuntimeException("Invalid property Mapping: " + desc); 
    182                 } 
    183  
    184                 otherType = app.getDbMapping(proto); 
    185  
    186                 if (otherType == null) { 
    187                     throw new RuntimeException("DbMapping for " + proto + 
    188                                                " not found from " + ownType.getTypeName()); 
    189                 } 
    190  
    191                 // make sure the type we're referring to is up to date! 
    192                 if (otherType.needsUpdate()) { 
    193                     otherType.update(); 
    194                 } 
    195  
    196             } else { 
    197                 virtual = false; 
    198                 columnName = desc; 
    199                 reftype = PRIMITIVE; 
    200             } 
    201         } 
    202  
    203         ResourceProperties config = props.getSubProperties(propName + '.'); 
    204  
    205         readonly = "true".equalsIgnoreCase(config.getProperty("readonly")); 
    206  
    207         isPrivate = "true".equalsIgnoreCase(config.getProperty("private")); 
     171                throw new RuntimeException("Invalid property Mapping: " + desc); 
     172            } 
     173 
     174            otherType = app.getDbMapping(proto); 
     175 
     176            if (otherType == null) { 
     177                throw new RuntimeException("DbMapping for " + proto + 
     178                                           " not found from " + ownType.getTypeName()); 
     179            } 
     180 
     181            // make sure the type we're referring to is up to date! 
     182            if (otherType.needsUpdate()) { 
     183                otherType.update(); 
     184            } 
     185 
     186        } 
     187 
     188        readonly = "true".equalsIgnoreCase(props.getProperty("readonly")); 
     189        isPrivate = "true".equalsIgnoreCase(props.getProperty("private")); 
    208190 
    209191        // the following options only apply to object and collection relations 
     
    211193            Vector newConstraints = new Vector(); 
    212194 
    213             parseOptions(newConstraints, config); 
     195            parseOptions(newConstraints, props); 
    214196 
    215197            constraints = new Constraint[newConstraints.size()]; 
     
    257239    } 
    258240 
    259     protected void parseOptions(Vector cnst, Properties config) { 
    260         String loading = config.getProperty("loadmode"); 
     241    /** 
     242     * Converts old style foo = collection(Bar) mapping to new style  
     243     * foo.collection = Bar mappinng and returns true if a non-primitive mapping 
     244     * was encountered. 
     245     * @param value the value of the top level property mapping 
     246     * @param config the sub-map for this property mapping 
     247     * @return true if the value describes a valid, non-primitive property mapping 
     248     */ 
     249    protected boolean parseDescriptor(Object value, Map config) { 
     250        String desc = value instanceof String ? (String) value : null; 
     251 
     252        if ((desc == null) || "".equals(desc.trim())) { 
     253            if (propName != null) { 
     254                reftype = PRIMITIVE; 
     255                columnName = propName; 
     256            } else { 
     257                reftype = INVALID; 
     258                columnName = propName; 
     259            } 
     260            return false; 
     261        } else { 
     262            desc = desc.trim(); 
     263 
     264            int open = desc.indexOf("("); 
     265            int close = desc.indexOf(")"); 
     266 
     267            if ((open > -1) && (close > open)) { 
     268                String ref = desc.substring(0, open).trim(); 
     269                String proto = desc.substring(open + 1, close).trim(); 
     270 
     271                if ("collection".equalsIgnoreCase(ref)) { 
     272                    config.put("collection", proto); 
     273                } else if ("mountpoint".equalsIgnoreCase(ref)) { 
     274                    config.put("mountpoint", proto); 
     275                } else if ("object".equalsIgnoreCase(ref)) { 
     276                    config.put("object", proto); 
     277                } else { 
     278                    throw new RuntimeException("Invalid property Mapping: " + desc); 
     279                } 
     280 
     281                return true; 
     282 
     283            } else { 
     284                virtual = false; 
     285                columnName = desc; 
     286                reftype = PRIMITIVE; 
     287                return false; 
     288            } 
     289        } 
     290 
     291    } 
     292 
     293    protected void parseOptions(Vector cnst, Properties props) { 
     294        String loading = props.getProperty("loadmode"); 
    261295 
    262296        aggressiveLoading = (loading != null) && 
    263297                            "aggressive".equalsIgnoreCase(loading.trim()); 
    264298 
    265         String caching = config.getProperty("cachemode"); 
     299        String caching = props.getProperty("cachemode"); 
    266300 
    267301        aggressiveCaching = (caching != null) && 
     
    269303 
    270304        // get order property 
    271         order = config.getProperty("order"); 
     305        order = props.getProperty("order"); 
    272306 
    273307        if ((order != null) && (order.trim().length() == 0)) { 
     
    276310 
    277311        // get the criteria(s) for updating this collection 
    278         updateCriteria = config.getProperty("updatecriteria"); 
     312        updateCriteria = props.getProperty("updatecriteria"); 
    279313 
    280314        // get the autosorting flag 
    281         autoSorted = "auto".equalsIgnoreCase(config.getProperty("sortmode")); 
     315        autoSorted = "auto".equalsIgnoreCase(props.getProperty("sortmode")); 
    282316 
    283317        // get additional filter property 
    284         filter = config.getProperty("filter"); 
     318        filter = props.getProperty("filter"); 
    285319 
    286320        if (filter != null) { 
     
    305339 
    306340        // get additional tables 
    307         additionalTables = config.getProperty("filter.additionalTables"); 
     341        additionalTables = props.getProperty("filter.additionalTables"); 
    308342 
    309343        if (additionalTables != null) { 
     
    335369 
    336370        // get query hints 
    337         queryHints = config.getProperty("hints"); 
     371        queryHints = props.getProperty("hints"); 
    338372 
    339373        // get max size of collection 
    340         String max = config.getProperty("maxSize"); 
    341  
    342         if (max != null) { 
    343             try { 
    344                 maxSize = Integer.parseInt(max); 
    345             } catch (NumberFormatException nfe) { 
    346                 maxSize = 0; 
    347             } 
    348         } else { 
    349             maxSize = 0; 
    350         } 
     374        maxSize = getIntegerProperty("maxSize", props, 0); 
     375        if (maxSize == 0) { 
     376            // use limit as alias for maxSize 
     377            maxSize = getIntegerProperty("limit", props, 0); 
     378        } 
     379        offset = getIntegerProperty("offset", props, 0); 
    351380 
    352381        // get group by property 
    353         groupby = config.getProperty("group"); 
     382        groupby = props.getProperty("group"); 
    354383 
    355384        if ((groupby != null) && (groupby.trim().length() == 0)) { 
     
    358387 
    359388        if (groupby != null) { 
    360             groupbyOrder = config.getProperty("group.order"); 
     389            groupbyOrder = props.getProperty("group.order"); 
    361390 
    362391            if ((groupbyOrder != null) && (groupbyOrder.trim().length() == 0)) { 
     
    364393            } 
    365394 
    366             groupbyPrototype = config.getProperty("group.prototype"); 
     395            groupbyPrototype = props.getProperty("group.prototype"); 
    367396 
    368397            if ((groupbyPrototype != null) && (groupbyPrototype.trim().length() == 0)) { 
     
    375404 
    376405        // check if subnode condition should be applied for property relations 
    377         accessName = config.getProperty("accessname"); 
     406        accessName = props.getProperty("accessname"); 
    378407 
    379408        // parse contstraints 
    380         String local = config.getProperty("local"); 
    381         String foreign = config.getProperty("foreign"); 
     409        String local = props.getProperty("local"); 
     410        String foreign = props.getProperty("foreign"); 
    382411 
    383412        if ((local != null) && (foreign != null)) { 
     
    388417        // parse additional contstraints from *.1 to *.9 
    389418        for (int i=1; i<10; i++) { 
    390             local = config.getProperty("local."+i); 
    391             foreign = config.getProperty("foreign."+i); 
     419            local = props.getProperty("local."+i); 
     420            foreign = props.getProperty("foreign."+i); 
    392421 
    393422            if ((local != null) && (foreign != null)) { 
     
    398427        // parse constraints logic 
    399428        if (cnst.size() > 1) { 
    400             String logic = config.getProperty("logicalOperator"); 
     429            String logic = props.getProperty("logicalOperator"); 
    401430            if ("and".equalsIgnoreCase(logic)) { 
    402431                logicalOperator = AND; 
     
    414443    } 
    415444 
     445    private int getIntegerProperty(String name, Properties props, int defaultValue) { 
     446        Object value = props.get(name); 
     447 
     448        if (value instanceof Number) { 
     449            return ((Number) value).intValue(); 
     450        } else if (value instanceof String) { 
     451            return Integer.parseInt((String) value); 
     452        } 
     453        return defaultValue; 
     454    } 
     455 
    416456    /////////////////////////////////////////////////////////////////////////////////////////// 
    417457 
     
    419459     * Get the configuration properties for this relation. 
    420460     */ 
    421     public ResourceProperties getConfig() { 
    422         return ownType.getProperties().getSubProperties(propName + '.'); 
     461    public Map getConfig() { 
     462        return ownType.getSubProperties(propName + '.'); 
    423463    } 
    424464 
     
    874914        } else if (useOrder && (order != null)) { 
    875915            q.append(" ORDER BY ").append(order); 
     916        } 
     917 
     918        if (maxSize > 0 && !ownType.isOracle()) { 
     919            q.append(" LIMIT ").append(maxSize); 
     920            if (offset > 0) { 
     921                q.append(" OFFSET ").append(offset); 
     922            } 
    876923        } 
    877924 
  • helma/helma/trunk/src/helma/scripting/rhino/HopObjectCtor.java

    r9327 r9557  
    1818import java.lang.reflect.Constructor; 
    1919import java.lang.reflect.Method; 
     20import java.util.Properties; 
    2021 
    2122import helma.objectmodel.INode; 
     
    5960        addAsConstructor(core.global, prototype); 
    6061        defineProperty("getById", new GetById(core.global), attr); 
     62        defineProperty("getCollection", new HopCollection(core.global), attr); 
    6163    } 
    6264 
     
    181183    } 
    182184 
     185    class HopCollection extends BaseFunction { 
     186 
     187        public HopCollection(Scriptable scope) { 
     188            ScriptRuntime.setFunctionProtoAndParent(this, scope); 
     189        } 
     190 
     191        public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { 
     192            if (args.length != 1) { 
     193                throw new IllegalArgumentException("Wrong number of arguments in definePrototype()"); 
     194            } 
     195            if (!(args[0] instanceof Scriptable)) { 
     196                throw new IllegalArgumentException("Second argument to HopObject.definePrototype() must be Object"); 
     197            } 
     198 
     199            Scriptable desc = (Scriptable) args[0]; 
     200            Properties childmapping = core.scriptableToProperties(desc); 
     201            if (!childmapping.containsKey("collection")) { 
     202                // if contained type isn't defined explicitly limit collection to our own type 
     203                childmapping.put("collection", HopObjectCtor.this.getFunctionName()); 
     204            } 
     205 
     206            Node node = new Node("HopQuery", null, core.app.getWrappedNodeManager()); 
     207            Properties props = new Properties(); 
     208            props.put("_children", childmapping); 
     209            DbMapping dbmap = new DbMapping(core.app, null, props); 
     210            dbmap.update(); 
     211            node.setDbMapping(dbmap); 
     212            node.setState(Node.VIRTUAL); 
     213            return new HopObject("HopQuery", core, node, core.hopObjectProto); 
     214        } 
     215 
     216        public int getArity() { 
     217            return 1; 
     218        } 
     219 
     220        public int getLength() { 
     221            return 1; 
     222        } 
     223    } 
     224 
    183225} 
  • helma/helma/trunk/src/helma/scripting/rhino/RhinoCore.java

    r9518 r9557  
    2424import helma.objectmodel.db.DbMapping; 
    2525import helma.scripting.*; 
    26 import helma.util.CacheMap; 
    27 import helma.util.SystemMap; 
    28 import helma.util.WrappedMap; 
    29 import helma.util.WeakCacheMap; 
     26import helma.util.*; 
    3027import org.mozilla.javascript.*; 
    3128import org.mozilla.javascript.tools.debugger.ScopeProvider; 
     
    217214     *  @param prototype the prototype to be created 
    218215     */ 
    219     private synchronized void initPrototype(Prototype prototype) { 
     216    protected synchronized TypeInfo initPrototype(Prototype prototype) { 
    220217 
    221218        String name = prototype.getName(); 
     
    237234                op = new HopObject(name, this); 
    238235            } 
    239             registerPrototype(prototype, op); 
     236            type = registerPrototype(prototype, op); 
    240237        } 
    241238 
     
    251248            } 
    252249        } 
     250 
     251        return type; 
    253252    } 
    254253 
     
    292291     *  @param type the prototype object info 
    293292     */ 
    294     private void setParentPrototype(Prototype prototype, TypeInfo type) { 
     293    protected void setParentPrototype(Prototype prototype, TypeInfo type) { 
    295294        String name = prototype.getName(); 
    296295        String lowerCaseName = prototype.getLowerCaseName(); 
     
    434433        TypeInfo type = getPrototypeInfo(protoName); 
    435434        SystemMap map = new SystemMap(); 
    436         Iterator it =    type.compiledProperties.iterator(); 
     435        Iterator it = type.compiledProperties.iterator(); 
    437436        while(it.hasNext()) { 
    438437            Object key = it.next(); 
     
    748747        return href; 
    749748    } 
     749 
     750 
     751    Properties scriptableToProperties(Scriptable obj) { 
     752        Object[] ids = obj.getIds(); 
     753        Properties props = new ResourceProperties(app, null, null, true); 
     754        for (int i = 0; i < ids.length; i++) { 
     755            // we ignore non-string keys 
     756            if (ids[i] instanceof String) { 
     757                String key = (String) ids[i]; 
     758                Object value = obj.get(key, obj); 
     759                if (value == Undefined.instance || value == Scriptable.NOT_FOUND) { 
     760                    value = null; 
     761                } else if (value instanceof Scriptable) { 
     762                    value = scriptableToProperties((Scriptable) value); 
     763                } 
     764                props.put(key, value); 
     765            } 
     766        } 
     767        return props; 
     768    }     
    750769 
    751770    /**