Source Code Cross Referenced for MMObjectNode.java in  » Database-ORM » MMBase » org » mmbase » module » core » 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 » Database ORM » MMBase » org.mmbase.module.core 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:
0003:        This software is OSI Certified Open Source Software.
0004:        OSI Certified is a certification mark of the Open Source Initiative.
0005:
0006:        The license (Mozilla version 1.0) can be read at the MMBase site.
0007:        See http://www.MMBase.org/license
0008:
0009:         */
0010:        package org.mmbase.module.core;
0011:
0012:        import java.util.*;
0013:        import java.io.*;
0014:
0015:        import org.mmbase.cache.*;
0016:        import org.mmbase.bridge.Field;
0017:        import org.mmbase.bridge.Node;
0018:        import org.mmbase.module.corebuilders.InsRel;
0019:        import org.mmbase.module.builders.DayMarkers;
0020:        import org.mmbase.security.*;
0021:        import org.mmbase.storage.search.*;
0022:        import org.mmbase.util.Casting;
0023:        import org.mmbase.util.SizeOf;
0024:        import org.mmbase.util.DynamicDate;
0025:        import org.mmbase.util.logging.*;
0026:        import org.mmbase.util.functions.*;
0027:        import org.w3c.dom.Document;
0028:
0029:        /**
0030:         * MMObjectNode is the core of the MMBase system.
0031:         * This class is what its all about, because the instances of this class hold the content we are using.
0032:         * All active Nodes with data and relations are MMObjectNodes and make up the
0033:         * object world that is MMBase (Creating, searching, removing is done by the node's parent,
0034:         * which is a class extended from MMObjectBuilder)
0035:         *
0036:         * @author Daniel Ockeloen
0037:         * @author Pierre van Rooden
0038:         * @author Eduard Witteveen
0039:         * @author Michiel Meeuwissen
0040:         * @author Ernst Bunders
0041:         * @version $Id: MMObjectNode.java,v 1.213 2008/02/03 17:33:57 nklasens Exp $
0042:         */
0043:
0044:        public class MMObjectNode implements  org.mmbase.util.SizeMeasurable,
0045:                java.io.Serializable {
0046:            private static final Logger log = Logging
0047:                    .getLoggerInstance(MMObjectNode.class);
0048:
0049:            /**
0050:             * Large fields (blobs) are loaded 'lazily', so only on explicit request. Until the first exlicit request this value is stored in such fields.
0051:             * It can be set back into the field with {@link #storeValue}, to unload the field again.
0052:             * @since MMBase-1.7.4
0053:             */
0054:            public final static String VALUE_SHORTED = "$SHORTED";
0055:
0056:            /**
0057:             * Map which stores the current database value for fields when
0058:             * then change in the node.
0059:             * it can be used to optimise cacheing
0060:             * @since MMBase-1.8
0061:             */
0062:            private Map<String, Object> oldValues = new HashMap<String, Object>();
0063:
0064:            /**
0065:             * Holds the name - value pairs of this node (the node's fields).
0066:             * Most nodes will have a 'number' and an 'otype' field, and fields which will differ by builder.
0067:             * This collection should not be directly queried or changed -
0068:             * use the SetValue and getXXXValue methods instead.
0069:             * It should then be made private, and methods that change the map (storeValue) be made synchronized.
0070:             * Note: To avoid synchronisation conflicts, we can't really change the type until the property is made private.
0071:             */
0072:            protected Map<String, Object> values = Collections
0073:                    .synchronizedMap(new HashMap<String, Object>());
0074:            private Map<String, Long> sizes = Collections
0075:                    .synchronizedMap(new HashMap<String, Long>());
0076:
0077:            /**
0078:             * Determines whether the node is being initialized (typically when it is loaded from the database).
0079:             * Use {@link #start} to start initializing, use {@link #finish} to end.
0080:             * @since MMBase-1.7
0081:             */
0082:            protected boolean initializing = false;
0083:
0084:            /**
0085:             * Holds the 'extra' name-value pairs (the node's properties)
0086:             * which are retrieved from the 'properties' table.
0087:             * @scope private
0088:             */
0089:            public Hashtable<String, MMObjectNode> properties;
0090:
0091:            /**
0092:             * Set which stores the keys of the fields that were changed
0093:             * since the last commit.
0094:             */
0095:            private Set<String> changed = Collections
0096:                    .synchronizedSet(new HashSet<String>());
0097:
0098:            /**
0099:             * Pointer to the parent builder that is responsible for this node.
0100:             * Note: this may on occasion (due to optimization) duffer for the node's original builder.
0101:             * Use {@link #getBuilder} instead.
0102:             * @scope private
0103:             */
0104:            protected MMObjectBuilder parent;
0105:
0106:            /**
0107:             * Pointer to the actual builder to which this node belongs.
0108:             * This value is initialised through the first call to {@link #getBuilder}
0109:             */
0110:            private MMObjectBuilder builder = null;
0111:
0112:            /**
0113:             * If <code>true</code>, the node is a new node, which is not (yet) stored in storage.
0114:             */
0115:            protected boolean isNew = false;
0116:
0117:            /**
0118:             * New aliases of the node
0119:             */
0120:            private Set<String> aliases = null;
0121:
0122:            // object to sync access to properties
0123:            private final Object properties_sync = new Object();
0124:
0125:            /**
0126:             * temporarily holds a new context for a node
0127:             * @since MMBase-1.7
0128:             */
0129:
0130:            private String newContext = null;
0131:
0132:            /**
0133:             * Default Main constructor, creates a node that is new and not (yet) in storage.
0134:             * @param parent the node's parent, an instance of the node's builder.
0135:             * @throws IllegalArgumentException If parent is <code>null</code>
0136:             */
0137:            public MMObjectNode(MMObjectBuilder parent) {
0138:                this (parent, true);
0139:            }
0140:
0141:            /**
0142:             * Main constructor.
0143:             * @param parent the node's parent, an instance of the node's builder.
0144:             * @param isNew if the node is a newly created node
0145:             * @throws IllegalArgumentException If parent is <code>null</code>
0146:             */
0147:            public MMObjectNode(MMObjectBuilder parent, boolean isNew) {
0148:                this .isNew = isNew;
0149:                if (parent != null) {
0150:                    this .parent = parent;
0151:                } else {
0152:                    throw new IllegalArgumentException(
0153:                            "Constructor called with parent=null");
0154:                }
0155:            }
0156:
0157:            /**
0158:             * @since MMBase-1.8
0159:             */
0160:            public MMObjectNode(MMObjectNode node) {
0161:                parent = node.parent;
0162:                isNew = node.isNew();
0163:                values.putAll(node.getValues());
0164:                values.putAll(node.getOldValues());
0165:            }
0166:
0167:            /**
0168:             * Creates an MMObject based on a given Map.  This can e.g. be used to make an MMObjectNode of a bridge node (use {@link org.mmbase.bridge.util.NodeMap}).
0169:             *
0170:             * @since MMBase-1.8
0171:             */
0172:            public MMObjectNode(MMObjectBuilder parent, Map<String, Object> map) {
0173:                isNew = false;
0174:                this .parent = parent;
0175:                values = map;
0176:            }
0177:
0178:            /**
0179:             * Returns the actual builder of the node.
0180:             * Note that it is possible that, due to optimization, a node is currently associated with
0181:             * another (parent) builder, i.e. a posrel node may be associated with a insrel builder.
0182:             * This method returns the actual builder.
0183:             * The node may miss vital information (not retrieved from the database) to act as a node of such
0184:             * a builder - if you need actual status you need to reload it.
0185:             * @since MMBase-1.6
0186:             * @return the builder of this node
0187:             */
0188:            public MMObjectBuilder getBuilder() {
0189:                if (builder == null) {
0190:                    int oType = getOType();
0191:                    if (oType == -1 || parent.getNumber() == oType) {
0192:                        builder = parent;
0193:                    } else {
0194:                        String builderName = parent.mmb.getTypeDef().getValue(
0195:                                oType);
0196:                        if (builderName != null) { // avoid NPE from mmb.getBuilder.
0197:                            builder = parent.mmb.getBuilder(builderName);
0198:                        }
0199:                    }
0200:                    if (builder == null) {
0201:                        log.warn("Builder of node " + getNumber()
0202:                                + " not found, taking 'object'");
0203:                        builder = parent.mmb.getBuilder("object");
0204:                    }
0205:                }
0206:                return builder;
0207:            }
0208:
0209:            /**
0210:             * Start the loading of a node
0211:             * @since MMBase-1.7
0212:             */
0213:            public void start() {
0214:                initializing = true;
0215:            }
0216:
0217:            /**
0218:             * Finish the loading of a node
0219:             * @since MMBase-1.7
0220:             */
0221:            public void finish() {
0222:                initializing = false;
0223:            }
0224:
0225:            /**
0226:             * Tests whether the data in a node is valid (throws an exception if this is not the case).
0227:             * @throws org.mmbase.module.core.InvalidDataException
0228:             *   If the data was unrecoverably invalid (the references did not point to existing objects)
0229:             */
0230:            public void testValidData() throws InvalidDataException {
0231:                parent.testValidData(this );
0232:            };
0233:
0234:            /**
0235:             * Commit the node to the database or other storage system.
0236:             * This can only be done on a existing (inserted) node. It will use the
0237:             * changed Vector as its base of what to commit/change.
0238:             * @return <code>true</code> if the commit was succesfull, <code>false</code> is it failed
0239:             */
0240:            public boolean commit() {
0241:                boolean success = parent.commit(this );
0242:                if (success) {
0243:                    isNew = false; // perhaps it is always already false (otherwise insert is called, I think), but no matter, now it certainly isn't new!
0244:                } else {
0245:                    values.putAll(oldValues);
0246:                }
0247:                oldValues.clear();
0248:                changed.clear();
0249:                return success;
0250:            }
0251:
0252:            /**
0253:             * Undo changes made to the node.
0254:             *
0255:             * @since MMBase-1.8
0256:             */
0257:            public void cancel() {
0258:                values.putAll(oldValues);
0259:                oldValues.clear();
0260:                changed.clear();
0261:            }
0262:
0263:            /**
0264:             * Insert this node into the storage
0265:             * @param userName the name of the user who inserts the node. This value is ignored
0266:             * @return the new node key (number field), or -1 if the insert failed
0267:             */
0268:            public int insert(String userName) {
0269:                return parent.insert(userName, this );
0270:            }
0271:
0272:            /**
0273:             * Insert this node into the database or other storage system.
0274:             * @param user the user who inserts the node.
0275:             *        Used to set security-related information
0276:             * @return the new node key (number field), or -1 if the insert failed
0277:             * @since MMBase-1.7
0278:             */
0279:            public int insert(UserContext user) {
0280:                int nodeID = parent.safeInsert(this , user.getIdentifier());
0281:                if (nodeID != -1) {
0282:                    MMBaseCop mmbaseCop = parent.getMMBase().getMMBaseCop();
0283:                    mmbaseCop.getAuthorization().create(user, nodeID);
0284:                    if (newContext != null) {
0285:                        mmbaseCop.getAuthorization().setContext(user, nodeID,
0286:                                newContext);
0287:                        newContext = null;
0288:                    }
0289:                }
0290:                return nodeID;
0291:            }
0292:
0293:            /**
0294:             * Commit this node to the storage
0295:             * @param user the user who commits the node.
0296:             *        Used to set security-related information
0297:             * @return <code>true</code> if succesful
0298:             * @since MMBase-1.7
0299:             */
0300:            public boolean commit(UserContext user) {
0301:                boolean success = parent.safeCommit(this );
0302:                if (success) {
0303:                    MMBaseCop mmbaseCop = parent.getMMBase().getMMBaseCop();
0304:                    mmbaseCop.getAuthorization().update(user, getNumber());
0305:                    if (newContext != null) {
0306:                        mmbaseCop.getAuthorization().setContext(user,
0307:                                getNumber(), newContext);
0308:                        newContext = null;
0309:                    }
0310:                }
0311:                return success;
0312:            }
0313:
0314:            /**
0315:             * Remove this node from the storage
0316:             * @param user the user who removes the node.
0317:             *        Used to set security-related information
0318:             * @since MMBase-1.7
0319:             */
0320:            public void remove(UserContext user) {
0321:                if (log.isDebugEnabled()) {
0322:                    log.debug("Deleting node " + getNumber() + " because "
0323:                            + Logging.stackTrace(5));
0324:                }
0325:                parent.removeNode(this );
0326:                parent.getMMBase().getMMBaseCop().getAuthorization().remove(
0327:                        user, getNumber());
0328:            }
0329:
0330:            /**
0331:             * Sets the security context for this node
0332:             * @param user the user who changes the context of the node.
0333:             * @param context the new context
0334:             * @param now if <code>true</code>, the context is changed instantly, otherwise it is changed
0335:             *        after the node is send to storage.
0336:             * @since MMBase-1.7
0337:             */
0338:            public void setContext(UserContext user, String context, boolean now) {
0339:                if (now) {
0340:                    parent.getMMBase().getMMBaseCop().getAuthorization()
0341:                            .setContext(user, getNumber(), context);
0342:                } else {
0343:                    newContext = context;
0344:                }
0345:            }
0346:
0347:            /**
0348:             * Returns the security context for this node
0349:             * @param user the user who requests the context of the node.
0350:             * @since MMBase-1.7.1
0351:             */
0352:            public String getContext(UserContext user) {
0353:                if (newContext != null)
0354:                    return newContext;
0355:                if (getNumber() < 0)
0356:                    return user.getOwnerField();
0357:                try {
0358:                    return parent.getMMBase().getMMBaseCop().getAuthorization()
0359:                            .getContext(user, getNumber());
0360:                } catch (Exception e) {
0361:                    log.warn(e);
0362:                    return getStringValue("owner");
0363:                }
0364:            }
0365:
0366:            /**
0367:             * Returns the possible new security contexts for this node
0368:             * @param user the user who requests the context of the node.
0369:             * @since MMBase-1.7.1
0370:             */
0371:            public Set<String> getPossibleContexts(UserContext user) {
0372:                if (getNumber() < 0) {
0373:                    // a new node has yet no context (except the default).
0374:                    // instead of searching the database for data, return a
0375:                    // standard set of values existing of the current context
0376:                    // and the contexts "system" and "admin".
0377:                    // A better way involves rewriting the security layer to accept
0378:                    // MMObjectNodes instead of node numbers
0379:                    Set<String> contexts = new HashSet<String>();
0380:                    contexts.add(getContext(user));
0381:                    contexts.add("admin");
0382:                    contexts.add("system");
0383:                    return contexts;
0384:                    /*
0385:                     NodeSearchQuery query = new NodeSearchQuery(parent);
0386:                     CoreField fieldDefs = parent.getField("owner");
0387:                     StepField field = query.getField(fieldDefs);
0388:                     BasicFieldValueConstraint cons = new BasicFieldValueConstraint(field, getContext(user));
0389:                     query.setMaxNumber(1);
0390:                     try {
0391:                     Iterator resultList = parent.getNodes(query).iterator();
0392:                     if (resultList.hasNext()) {
0393:                     return ((MMObjectNode) resultList.next()).getPossibleContexts(user);
0394:                     }
0395:                     } catch (SearchQueryException sqe) {
0396:                     log.error(sqe.toString());
0397:                     }
0398:                     return new HashSet();
0399:                     */
0400:                }
0401:                return parent.getMMBase().getMMBaseCop().getAuthorization()
0402:                        .getPossibleContexts(user, getNumber());
0403:            }
0404:
0405:            /**
0406:             * Returns the core of this node in a string.
0407:             * Used for debugging.
0408:             * For data exchange use toXML() and getDTD().
0409:             * @return the contents of the node as a string.
0410:             */
0411:            public String toString() {
0412:                if (parent != null) {
0413:                    return parent.toString(this );
0414:                } else {
0415:                    return defaultToString();
0416:                }
0417:            }
0418:
0419:            /**
0420:             * @since MMBase-1.6.2
0421:             */
0422:            String defaultToString() {
0423:                StringBuilder result = new StringBuilder();
0424:                try {
0425:                    Set<Map.Entry<String, Object>> entrySet = values.entrySet();
0426:                    synchronized (values) {
0427:                        Iterator<Map.Entry<String, Object>> i = entrySet
0428:                                .iterator();
0429:                        while (i.hasNext()) {
0430:                            Map.Entry<String, Object> entry = i.next();
0431:                            String key = entry.getKey();
0432:                            String value = "" + entry.getValue(); // XXX:should be retrieveValue ?
0433:                            if (result.length() == 0) {
0434:                                result.append(key).append("='").append(value)
0435:                                        .append("'");
0436:                            } else {
0437:                                result.append(",").append(key).append("='")
0438:                                        .append(value).append("'");
0439:                            }
0440:                        }
0441:                    }
0442:                } catch (Throwable e) {
0443:                    result.append(values); // simpler version...
0444:                }
0445:                result.append(super .toString());
0446:                return result.toString();
0447:            }
0448:
0449:            /**
0450:             * @return <code>true</code> if field exists and may be used.
0451:             * @since MMBase-1.8
0452:             */
0453:            protected boolean checkFieldExistance(String fieldName) {
0454:                if (fieldName.charAt(0) == '_') {
0455:                    // don't complain then, a lot of hackery (apps1 import/export) is based on this.
0456:                    // This is just a hack to make app1 import/export working, withough exposing the values map.
0457:                    return true;
0458:                }
0459:                if (fieldName.indexOf('(') > 0) {
0460:                    return true;
0461:                }
0462:                if (!getBuilder().hasField(fieldName)) {
0463:                    if (MMBase.getMMBase().inDevelopment()) {
0464:                        throw new IllegalArgumentException(
0465:                                "You cannot use non-existing field '"
0466:                                        + fieldName + "' of node '"
0467:                                        + getNumber()
0468:                                        + "' existing fields of '"
0469:                                        + getBuilder().getTableName()
0470:                                        + "' are "
0471:                                        + getBuilder().getFieldNames());
0472:                    } else {
0473:                        log.warn("Tried to use non-existing field '"
0474:                                + fieldName + "' of node '" + getNumber()
0475:                                + "' from " + getBuilder().getTableName());
0476:                        log.warn(Logging.applicationStacktrace());
0477:                        return false;
0478:                    }
0479:                }
0480:                return true;
0481:            }
0482:
0483:            /**
0484:             * Stores a value in the values hashtable.
0485:             * This is a low-level method that circumvents typechecking and the triggers of extended classes.
0486:             * You should normally call {@link #setValue} to change fields.
0487:             * @todo This should become a synchronized method, once values becomes a private HashMap instead of a
0488:             * public Hashtable.
0489:             *
0490:             *@param fieldName the name of the field to change
0491:             *@param fieldValue the value to assign
0492:             */
0493:            public void storeValue(String fieldName, Object fieldValue) {
0494:                if (fieldName.startsWith("_") && fieldValue == null) {
0495:                    // This is just a hack to make app1 import/export working, withough exposing the values map.
0496:                    values.remove(fieldName);
0497:                }
0498:                if (checkFieldExistance(fieldName)) {
0499:                    values.put(fieldName, fieldValue);
0500:                }
0501:            }
0502:
0503:            /**
0504:             * this method stores a fieldvalue only once. the purpose is to
0505:             * store the value only the first time a field changes, so it reflects
0506:             * the value in the database.
0507:             * @param fieldName
0508:             * @param object
0509:             * @since MMBase-1.8
0510:             */
0511:            private void storeOldValue(String fieldName, Object object) {
0512:                if (!oldValues.containsKey(fieldName)) {
0513:                    oldValues.put(fieldName, object);
0514:                }
0515:            }
0516:
0517:            /**
0518:             * Retrieves a value from the values hashtable.
0519:             * This is a low-level method that circumvents typechecking and the triggers of extended classes.
0520:             * You should normally call {@link #getValue} to load fields.
0521:             *
0522:             * @param fieldName the name of the field to change
0523:             * @return the value of the field
0524:             */
0525:            public Object retrieveValue(String fieldName) {
0526:                return values.get(fieldName);
0527:            }
0528:
0529:            /**
0530:             * Determines whether the node is virtual.
0531:             * A virtual node is not persistent (that is, stored in a database table).
0532:             */
0533:            public boolean isVirtual() {
0534:                return false;
0535:            }
0536:
0537:            /**
0538:             * If a node is still 'new' you must persistify it with {@link #insert}, and otherwise with {@link #commit}.
0539:             * @since MMBase-1.8
0540:             */
0541:            public boolean isNew() {
0542:                return isNew;
0543:            }
0544:
0545:            /*
0546:             *
0547:             * @since MMBase-1.6
0548:             */
0549:
0550:            protected Document toXML(Object value, String fieldName) {
0551:                Document doc = Casting.toXML(value);
0552:                if (doc == null && parent.getField(fieldName).isRequired()) {
0553:                    doc = Casting.toXML("<p/>");
0554:                }
0555:                return doc;
0556:            }
0557:
0558:            /**
0559:             *  Sets a key/value pair in the main values of this node.
0560:             *  Note that if this node is a node in cache, the changes are immediately visible to
0561:             *  everyone, even if the changes are not committed.
0562:             *  The fieldName is added to the (public) 'changed' vector to track changes.
0563:             *  @param fieldName the name of the field to change
0564:             *  @param fieldValue the value to assign
0565:             *  @return <code>true</code> When the field was changed, false otherwise.
0566:             */
0567:            public boolean setValue(String fieldName, Object fieldValue) {
0568:                // check the value also when the parent thing is null
0569:                Object originalValue = values.get(fieldName);
0570:
0571:                if (fieldValue != VALUE_SHORTED) {
0572:                    // make sure this value remains not in the blob-cache.
0573:                    BlobCache blobs = parent.getBlobCache(fieldName);
0574:                    blobs.remove(blobs.getKey(getNumber(), fieldName));
0575:                }
0576:
0577:                if (fieldValue instanceof  DynamicDate) {
0578:                    // 'dynamic' values can of course not be stored in database, and that is not the intentention too, so
0579:                    // store a static version
0580:                    fieldValue = new Date(((Date) fieldValue).getTime());
0581:                }
0582:
0583:                if (log.isDebugEnabled()) {
0584:                    String string;
0585:                    if (fieldValue instanceof  byte[]) {
0586:                        string = "byte array of size "
0587:                                + ((byte[]) fieldValue).length;
0588:                    } else {
0589:                        string = Casting.toString(fieldValue);
0590:                        if (string.length() > 200)
0591:                            string = string.substring(0, 200);
0592:                    }
0593:                    log.debug("Setting " + fieldName + " to " + string);
0594:                }
0595:
0596:                boolean changed = (!values.containsKey(fieldName))
0597:                        || (originalValue == null ? fieldValue != null
0598:                                : !Casting.equals(originalValue, fieldValue));
0599:                if (!changed)
0600:                    return false;
0601:
0602:                if (log.isDebugEnabled()) {
0603:                    log.debug("" + fieldName + ":" + originalValue + " --> "
0604:                            + fieldValue);
0605:                }
0606:
0607:                //store the old value
0608:                storeOldValue(fieldName, originalValue);
0609:
0610:                // put the key/value in the value hashtable
0611:                storeValue(fieldName, fieldValue);
0612:                if (fieldValue instanceof  byte[]) {
0613:                    setSize(fieldName, ((byte[]) fieldValue).length);
0614:                    log.debug("Setting length to "
0615:                            + ((byte[]) fieldValue).length);
0616:                }
0617:
0618:                // process the changed value (?)
0619:                if (parent != null) {
0620:                    if (!parent.setValue(this , fieldName, originalValue)) {
0621:                        // setValue of parent returned false, no update needed...
0622:                        return false;
0623:                    }
0624:                } else {
0625:                    log.error("parent was null for node with number"
0626:                            + getNumber());
0627:                }
0628:                setUpdate(fieldName);
0629:                return true;
0630:            }
0631:
0632:            /**
0633:             * Sets the size (in byte) of the given field. This is meant for byte-array fields, which you
0634:             * fill using an InputStream.
0635:             * @see #getSize(String)
0636:             * @since MMBase-1.8
0637:             */
0638:            public void setSize(String fieldName, long size) {
0639:                sizes.put(fieldName, size);
0640:            }
0641:
0642:            /**
0643:             * Returns the size (in byte) of the given field. This is mainly targeted at fields of the type
0644:             * byte array. For other fields this method will return something reasonable, but it is as yet
0645:             * not well defined what...
0646:             *
0647:             * @since MMBase-1.8
0648:             */
0649:            public long getSize(String fieldName) {
0650:                Long l = sizes.get(fieldName);
0651:                if (l != null)
0652:                    return l;
0653:                Object value = values.get(fieldName);
0654:                // Value is null so it does not occupy any space.
0655:                if (value == null) {
0656:                    checkFieldExistance(fieldName);
0657:                    return 0;
0658:                }
0659:                // Value is not yet loaded from the database?
0660:                if (VALUE_SHORTED.equals(value))
0661:                    return -1;
0662:                return SizeOf.getByteSize(value);
0663:            }
0664:
0665:            // Add the field to update to the changed Vector
0666:            //
0667:            private void setUpdate(String fieldName) {
0668:                // obtain the type of field this is
0669:                int state = getDBState(fieldName);
0670:
0671:                // add it to the changed vector so we know that we have to update it
0672:                // on the next commit
0673:                if (!initializing && state != Field.STATE_VIRTUAL) {
0674:                    changed.add(fieldName);
0675:                }
0676:                // is it a memory only field ? then send a fieldchange
0677:                if (state == 0)
0678:                    sendFieldChangeSignal(fieldName);
0679:            }
0680:
0681:            /**
0682:             * Retrieve an object's number.
0683:             * In case of a new node that is not committed, this will return -1.
0684:             * @return the number of the node
0685:             */
0686:            public int getNumber() {
0687:                return Casting.toInt(values.get(MMObjectBuilder.FIELD_NUMBER));
0688:            }
0689:
0690:            /**
0691:             * Retrieve an object's object type.
0692:             * This is a number (an index in the typedef builer), rather than a name.
0693:             * @return the object type number of the node
0694:             */
0695:            public int getOType() {
0696:                return Casting.toInt(values
0697:                        .get(MMObjectBuilder.FIELD_OBJECT_TYPE));
0698:            }
0699:
0700:            /**
0701:             * @since MMBase-1.8
0702:             */
0703:            public boolean isNull(String fieldName) {
0704:                if (checkFieldExistance(fieldName)) {
0705:                    Field field = getBuilder().getField(fieldName);
0706:                    if (field.isVirtual()) {
0707:                        return false;
0708:                    }
0709:                    if (field != null && field.getType() == Field.TYPE_NODE) {
0710:                        return getIntValue(fieldName) <= -1;
0711:                    }
0712:                    Object value = values.get(fieldName);
0713:                    if (VALUE_SHORTED.equals(value)) {
0714:                        // value is not loaded from the database. We have to check the database to be sure.
0715:                        value = getValue(fieldName);
0716:                    }
0717:                    return value == null;
0718:                } else {
0719:                    return true;
0720:                }
0721:            }
0722:
0723:            /**
0724:             * Get a value of a certain field.
0725:             * @performance do not store byte values directly in node (?)
0726:             * @param fieldName the name of the field who's data to return
0727:             * @return the field's value as an <code>Object</code>
0728:             */
0729:            public Object getValue(String fieldName) {
0730:                // get the value from the values table
0731:                Object value = values.get(fieldName);
0732:
0733:                // explicitly load byte values if they are 'shortened'
0734:                if (VALUE_SHORTED.equals(value)) { // could use == if we are sure that everybody uses the constant
0735:
0736:                    BlobCache blobs = parent.getBlobCache(fieldName);
0737:                    String key = blobs.getKey(getNumber(), fieldName);
0738:                    value = blobs.get(key);
0739:                    if (value == null) {
0740:                        int type = getDBType(fieldName);
0741:                        switch (type) {
0742:                        case Field.TYPE_BINARY:
0743:                            value = parent.getShortedByte(fieldName, this );
0744:                            break;
0745:                        case Field.TYPE_STRING:
0746:                            value = parent.getShortedText(fieldName, this );
0747:                            break;
0748:                        default:
0749:                            throw new UnsupportedOperationException(
0750:                                    "Found shorted value for type " + type);
0751:                        }
0752:                        blobs.put(key, value);
0753:                    }
0754:                }
0755:
0756:                // if we have an XML-dbtype field, we always have to return a Document (or null).
0757:                // note that if the value is null we store it as a null value
0758:                if (parent != null && value != null
0759:                        && !(value instanceof  Document)
0760:                        && getDBType(fieldName) == Field.TYPE_XML) {
0761:                    String string = Casting.toString(value).trim();
0762:                    Document doc = toXML(string, fieldName);
0763:                    if (doc != null) {
0764:                        // store the document inside the field.. much faster...
0765:                        value = doc;
0766:                        values.put(fieldName, value);
0767:                    } else {
0768:                        values.put(fieldName, null);
0769:                    }
0770:                }
0771:
0772:                // routine to check for indirect values
0773:                // this are used for functions for example
0774:                // its implemented per builder so lets give this
0775:                // request to our builder
0776:                if (value == null) {
0777:                    value = parent.getValue(this , fieldName);
0778:                }
0779:                // still null!
0780:                if (value == null) {
0781:                    if (!checkFieldExistance(fieldName))
0782:                        return null;
0783:                }
0784:
0785:                if (value instanceof  InputStream) {
0786:                    value = useInputStream(fieldName, (InputStream) value);
0787:                }
0788:
0789:                // return the found object
0790:                return value;
0791:            }
0792:
0793:            /**
0794:             * Get a value of a certain field.  The value is returned as a
0795:             * String. Non-string values are automatically converted to
0796:             * String. 'null' is converted to an empty string.
0797:             * @param fieldName the name of the field who's data to return
0798:             * @return the field's value as a <code>String</code>
0799:             */
0800:            public String getStringValue(String fieldName) {
0801:                Object value = getValue(fieldName);
0802:                if (value instanceof  MMObjectNode)
0803:                    return "" + ((MMObjectNode) value).getNumber();
0804:                String s = Casting.toString(value);
0805:                return s;
0806:            }
0807:
0808:            /**
0809:             * XXX: return type of this method make it impossible to make MMObjectNode implements Node, perhaps it needs change
0810:             * @javadoc
0811:             * @since MMBase-1.6
0812:             */
0813:            public Object getFunctionValue(String functionName,
0814:                    List<?> parameters) {
0815:                return parent.getFunctionValue(this , functionName, parameters);
0816:            }
0817:
0818:            /**
0819:             * @javadoc
0820:             * @since MMBase-1.8
0821:             */
0822:            public Parameters createParameters(String functionName) {
0823:                return parent.createParameters(functionName);
0824:            }
0825:
0826:            /**
0827:             * @javadoc
0828:             * @since MMBase-1.8
0829:             */
0830:            public Function<?> getFunction(String functionName) {
0831:                return parent.getFunction(this , functionName);
0832:            }
0833:
0834:            /**
0835:             * @javadoc
0836:             * @since MMBase-1.8
0837:             */
0838:            public Collection<Function<?>> getFunctions() {
0839:                return parent.getFunctions(this );
0840:            }
0841:
0842:            /**
0843:             * Returns the value of the specified field as a <code>dom.Document</code>
0844:             * If the node value is not itself a Document, the method attempts to
0845:             * attempts to convert the String value into an XML.
0846:             * If the value cannot be converted, this method returns <code>null</code>
0847:             *
0848:             * @param fieldName  the name of the field to be returned
0849:             * @return  the value of the specified field as a DOM Element or <code>null</code>
0850:             * @throws  IllegalArgumentException if the value cannot be converted.
0851:             * @since MMBase-1.6
0852:             */
0853:            public Document getXMLValue(String fieldName) {
0854:                Document o = toXML(getValue(fieldName), fieldName);
0855:                if (o != null && getDBType(fieldName) == Field.TYPE_XML) {
0856:                    storeValue(fieldName, o);
0857:                }
0858:                return o;
0859:            }
0860:
0861:            /**
0862:             * If the values map contains an InputStream, care must be taken because often an InputStream can be used only once.
0863:             * @since MMBase-1.8
0864:             */
0865:            private byte[] useInputStream(String fieldName, InputStream stream) { // first, convert to byte-array
0866:                ByteArrayOutputStream bos = new ByteArrayOutputStream();
0867:                try {
0868:                    byte[] buf = new byte[1024];
0869:                    int n;
0870:                    while ((n = stream.read(buf)) > -1) {
0871:                        bos.write(buf, 0, n);
0872:                    }
0873:                } catch (IOException ioe) {
0874:                    log.error(ioe);
0875:                }
0876:                byte[] b = bos.toByteArray();
0877:                // check if we can cache it.
0878:                BlobCache blobs = parent.getBlobCache(fieldName);
0879:                String key = blobs.getKey(getNumber(), fieldName);
0880:                if (b.length < blobs.getMaxEntrySize()) {
0881:                    blobs.put(key, b);
0882:                }
0883:                setSize(fieldName, b.length);
0884:                values.put(fieldName, b);
0885:                return b;
0886:            }
0887:
0888:            /**
0889:             * Get a binary value of a certain field.
0890:             * @performance do not store byte values directly in node (?)
0891:             * @param fieldName the name of the field who's data to return
0892:             * @return the field's value as an <code>byte []</code> (binary/blob field)
0893:             */
0894:            public byte[] getByteValue(String fieldName) {
0895:                Object obj = getValue(fieldName);
0896:                if (obj == null) {
0897:                    return new byte[0];
0898:                } else if (obj instanceof  byte[]) {
0899:                    // was already unmapped so return the value
0900:                    return (byte[]) obj;
0901:                } else {
0902:                    byte[] b;
0903:                    if (getDBType(fieldName) == Field.TYPE_STRING) {
0904:                        String s = getStringValue(fieldName);
0905:                        try {
0906:                            b = s.getBytes(parent.getMMBase().getEncoding());
0907:                        } catch (UnsupportedEncodingException uee) {
0908:                            log.error(uee.getMessage());
0909:                            b = s.getBytes();
0910:                        }
0911:                    } else {
0912:                        b = new byte[0];
0913:                    }
0914:                    return b;
0915:                }
0916:            }
0917:
0918:            public InputStream getInputStreamValue(String fieldName) {
0919:                Object value = getValue(fieldName);
0920:                if (value == null) {
0921:                    checkFieldExistance(fieldName);
0922:                    log.debug("NULL on " + fieldName + " " + this ,
0923:                            new Exception());
0924:                    return new ByteArrayInputStream(new byte[0]);
0925:                } else {
0926:                    if (log.isTraceEnabled()) {
0927:                        log.trace("Found " + value);
0928:                    }
0929:                }
0930:
0931:                if (value instanceof  InputStream) {
0932:                    // cannot return it directly, it would kill the inputstream, and perhaps it cannot be saved in db anymore then.
0933:                    // Sad, we have a buffer always now.
0934:                    // XXX think of something that the buffer is only needed if actually used a second time
0935:                    //     help-file, i think
0936:                    return new ByteArrayInputStream(useInputStream(fieldName,
0937:                            (InputStream) value));
0938:                }
0939:
0940:                if (VALUE_SHORTED.equals(value)) {
0941:                    BlobCache blobs = parent.getBlobCache(fieldName);
0942:                    String key = blobs.getKey(getNumber(), fieldName);
0943:                    byte[] v = (byte[]) blobs.get(key);
0944:                    if (v == null) {
0945:                        if (getSize(fieldName) < blobs.getMaxEntrySize()) {
0946:                            v = parent.mmb.getStorageManager().getBinaryValue(
0947:                                    this , parent.getField(fieldName));
0948:                            if (log.isDebugEnabled()) {
0949:                                log.debug("Putting in blob cache " + key);
0950:                            }
0951:                            blobs.put(key, v);
0952:                        } else {
0953:                            log
0954:                                    .debug("Too big for cache, requesting InputStream directly from storage");
0955:                            return parent.mmb.getStorageManager()
0956:                                    .getInputStreamValue(this ,
0957:                                            parent.getField(fieldName));
0958:                        }
0959:                    } else {
0960:                        log.debug("Found in blob cache " + fieldName);
0961:                    }
0962:                    return new ByteArrayInputStream(v);
0963:                } else {
0964:                    if (value instanceof  byte[]) {
0965:                        return new ByteArrayInputStream((byte[]) value);
0966:                    } else {
0967:                        // probably not a byte-array field, do something.
0968:                        // this behavior is undefined!, don't depend on it.
0969:                        return new ByteArrayInputStream(("" + value).getBytes());
0970:                    }
0971:                }
0972:            }
0973:
0974:            /**
0975:             * Get a value of a certain field.
0976:             * The value is returned as an MMObjectNode.
0977:             * If the field contains an Numeric value, the method
0978:             * tries to obtrain the object with that number.
0979:             * If it is a String, the method tries to obtain the object with
0980:             * that alias. The only other possible values are those created by
0981:             * certain virtual fields.
0982:             * All remaining situations return <code>null</code>.
0983:             * @param fieldName the name of the field who's data to return
0984:             * @return the field's value as an <code>int</code>
0985:             */
0986:            public MMObjectNode getNodeValue(String fieldName) {
0987:                if (fieldName == null
0988:                        || fieldName.equals(MMObjectBuilder.FIELD_NUMBER))
0989:                    return this ;
0990:                Object value = getValue(fieldName);
0991:                MMObjectNode res = null;
0992:                if (value instanceof  MMObjectNode) {
0993:                    res = (MMObjectNode) value;
0994:                } else if (value instanceof  Node) {
0995:                    Node node = (Node) value;
0996:                    if (node.isNew()) {
0997:                        throw new UnsupportedOperationException(
0998:                                "dropped tmpnodemanager...");
0999:                    } else if (value instanceof  org.mmbase.bridge.implementation.VirtualNode) {
1000:                        res = new VirtualNode(
1001:                                new org.mmbase.bridge.util.NodeMap(node));
1002:                    } else {
1003:                        res = parent.getNode(node.getNumber());
1004:                    }
1005:                } else if (value instanceof  Number) {
1006:                    int nodenumber = ((Number) value).intValue();
1007:                    if (nodenumber != -1) {
1008:                        res = parent.getNode(nodenumber);
1009:                    }
1010:                } else if (value != null && !value.equals("")) {
1011:                    res = parent.getNode(value.toString());
1012:                }
1013:                return res;
1014:            }
1015:
1016:            /**
1017:             * Get a value of a certain field.
1018:             * The value is returned as an int value. Values of non-int, numeric fields are converted if possible.
1019:             * Booelan fields return 0 for false, 1 for true.
1020:             * String fields are parsed to a number, if possible.
1021:             * If a value is an MMObjectNode, its numberfield is returned.
1022:             * All remaining field values return -1.
1023:             * @param fieldName the name of the field who's data to return
1024:             * @return the field's value as an <code>int</code>
1025:             */
1026:            public int getIntValue(String fieldName) {
1027:                Object value = getValue(fieldName);
1028:                if (value instanceof  MMObjectNode)
1029:                    return ((MMObjectNode) value).getNumber();
1030:                return Casting.toInt(value);
1031:            }
1032:
1033:            /**
1034:             * Get a value of a certain field.
1035:             * The value is returned as an boolean value.
1036:             * If the actual value is numeric, this call returns <code>true</code>
1037:             * if the value is a positive, non-zero, value. In other words, values '0'
1038:             * and '-1' are concidered <code>false</code>.
1039:             * If the value is a string, this call returns <code>true</code> if
1040:             * the value is "true" or "yes" (case-insensitive).
1041:             * In all other cases (including calling byte fields), <code>false</code>
1042:             * is returned.
1043:             * Note that there is currently no basic MMBase boolean type, but some
1044:             * <code>excecuteFunction</code> calls may return a Boolean result.
1045:             *
1046:             * @param fieldName the name of the field who's data to return
1047:             * @return the field's value as an <code>int</code>
1048:             */
1049:            public boolean getBooleanValue(String fieldName) {
1050:                return Casting.toBoolean(getValue(fieldName));
1051:            }
1052:
1053:            /**
1054:             * Get a value of a certain field.
1055:             * The value is returned as an Integer value. Values of non-Integer, numeric fields are converted if possible.
1056:             * Boolean fields return 0 for false, 1 for true.
1057:             * String fields are parsed to a number, if possible.
1058:             * All remaining field values return -1.
1059:             * @param fieldName the name of the field who's data to return
1060:             * @return the field's value as an <code>Integer</code>
1061:             */
1062:            public Integer getIntegerValue(String fieldName) {
1063:                Object value = getValue(fieldName);
1064:                if (value instanceof  MMObjectNode)
1065:                    return ((MMObjectNode) value).getNumber();
1066:                return Casting.toInteger(value);
1067:            }
1068:
1069:            /**
1070:             * Get a value of a certain field.
1071:             * @see #getValue
1072:             * @see Casting#toLong
1073:             * @param fieldName the name of the field who's data to return
1074:             * @return the field's value as a <code>long</code>
1075:             */
1076:            public long getLongValue(String fieldName) {
1077:                Object value = getValue(fieldName);
1078:                if (value instanceof  MMObjectNode)
1079:                    return ((MMObjectNode) value).getNumber();
1080:                return Casting.toLong(value);
1081:            }
1082:
1083:            /**
1084:             * Get a value of a certain field.
1085:             * The value is returned as a float value. Values of non-float, numeric fields are converted if possible.
1086:             * Boolean fields return 0 for false, 1 for true.
1087:             * String fields are parsed to a number, if possible.
1088:             * All remaining field values return -1.
1089:             * @param fieldName the name of the field who's data to return
1090:             * @return the field's value as a <code>float</code>
1091:             */
1092:            public float getFloatValue(String fieldName) {
1093:                Object value = getValue(fieldName);
1094:                if (value instanceof  MMObjectNode)
1095:                    return ((MMObjectNode) value).getNumber();
1096:                return Casting.toFloat(value);
1097:            }
1098:
1099:            /**
1100:             * Get a value of a certain field.
1101:             * The value is returned as a double value. Values of non-double, numeric fields are converted if possible.
1102:             * Boolean fields return 0 for false, 1 for true.
1103:             * String fields are parsed to a number, if possible.
1104:             * All remaining field values return -1.
1105:             * @param fieldName the name of the field who's data to return
1106:             * @return the field's value as a <code>double</code>
1107:             */
1108:            public double getDoubleValue(String fieldName) {
1109:                Object value = getValue(fieldName);
1110:                if (value instanceof  MMObjectNode)
1111:                    return ((MMObjectNode) value).getNumber();
1112:                return Casting.toDouble(value);
1113:            }
1114:
1115:            /**
1116:             * Get a value of a certain field.
1117:             * The value is returned as a Date value. Values of numeric fields are converted as if they were
1118:             * time in seconds since 1/1/1970.
1119:             * String fields are parsed to a date, if possible.
1120:             * All remaining field values return -1.
1121:             * @since MMBase-1.8
1122:             * @param fieldName the name of the field who's data to return
1123:             * @return the field's value as a <code>Date</code>
1124:             */
1125:            public Date getDateValue(String fieldName) {
1126:                Object value = getValue(fieldName);
1127:                org.mmbase.core.CoreField cf = getBuilder().getField(fieldName);
1128:                if (cf != null && cf.getType() == Field.TYPE_NODE) {
1129:                    // cannot be handled by casting, because it would receive object-number and cannot make distinction with Nodes.
1130:                    return new Date(-1);
1131:                }
1132:                return Casting.toDate(value);
1133:            }
1134:
1135:            /**
1136:             * Get a value of a certain field.
1137:             * The value is returned as a List value.
1138:             * Strings are treated as comma-seperated value lists, and split into their component parts.
1139:             * Values of other fields are returned as Lists of one object.
1140:             * @since MMBase-1.8
1141:             * @param fieldName the name of the field who's data to return
1142:             * @return the field's value as a <code>List</code>
1143:             */
1144:            public List getListValue(String fieldName) {
1145:                return Casting.toList(getValue(fieldName));
1146:            }
1147:
1148:            /**
1149:             * Returns the DBType of a field.
1150:             * @param fieldName the name of the field which' type to return
1151:             * @return the field's DBType
1152:             */
1153:            public int getDBType(String fieldName) {
1154:                return parent.getDBType(fieldName);
1155:            }
1156:
1157:            /**
1158:             * Returns the DBState of a field.
1159:             * @param fieldName the name of the field who's state to return
1160:             * @return the field's DBState
1161:             */
1162:            public int getDBState(String fieldName) {
1163:                if (parent != null) {
1164:                    return parent.getDBState(fieldName);
1165:                } else {
1166:                    return Field.STATE_UNKNOWN;
1167:                }
1168:            }
1169:
1170:            /**
1171:             * Return the names of all persistent fields that were changed.
1172:             * Note that this is a direct reference. Changes (i.e. clearing the vector) will affect the node's status.
1173:             * @return A Set containing Strings. The set is modifiable, and synchronized. Don't modify it though.
1174:             */
1175:            public Set<String> getChanged() {
1176:                return changed;
1177:            }
1178:
1179:            /**
1180:             * Tests whether one of the values of this node was changed since the last commit/insert.
1181:             * @return <code>true</code> if changes have been made, <code>false</code> otherwise
1182:             */
1183:            public boolean isChanged() {
1184:                return newContext != null || changed.size() > 0;
1185:            }
1186:
1187:            /**
1188:             * Clear the 'signal' Vector with the changed keys since last commit/insert.
1189:             * Marks the node as 'unchanged'.
1190:             * Does not affect the values of the fields, nor does it commit the node.
1191:             * @return always <code>true</code>
1192:             */
1193:            public boolean clearChanged() {
1194:                changed.clear();
1195:                oldValues.clear();
1196:                return true;
1197:            }
1198:
1199:            /**
1200:             * Deletes the propertie cache for this node.
1201:             * Forces a reload of the properties on next use.
1202:             */
1203:            public void delPropertiesCache() {
1204:                synchronized (properties_sync) {
1205:                    properties = null;
1206:                }
1207:            }
1208:
1209:            public Map<String, Object> getValues() {
1210:                return Collections.unmodifiableMap(values);
1211:            }
1212:
1213:            /**
1214:             * @since MMBase-1.8
1215:             */
1216:            public Map<String, Object> getOldValues() {
1217:                return Collections.unmodifiableMap(oldValues);
1218:            }
1219:
1220:            /**
1221:             * Return a the properties for this node.
1222:             * @return the properties as a <code>Hashtable</code>
1223:             */
1224:            public Hashtable<String, MMObjectNode> getProperties() {
1225:                synchronized (properties_sync) {
1226:                    if (properties == null) {
1227:                        properties = new Hashtable<String, MMObjectNode>();
1228:                        MMObjectBuilder bul = parent.mmb
1229:                                .getMMObject("properties");
1230:                        Enumeration<MMObjectNode> e = bul.search("parent=="
1231:                                + getNumber());
1232:                        while (e.hasMoreElements()) {
1233:                            MMObjectNode pnode = e.nextElement();
1234:                            String key = pnode.getStringValue("key");
1235:                            properties.put(key, pnode);
1236:                        }
1237:                    }
1238:                }
1239:                return properties;
1240:            }
1241:
1242:            /**
1243:             * Returns a specified property of this node.
1244:             * @param key the name of the property to retrieve
1245:             * @return the property object as a <code>MMObjectNode</code>
1246:             */
1247:            public MMObjectNode getProperty(String key) {
1248:                MMObjectNode n;
1249:                synchronized (properties_sync) {
1250:                    if (properties == null) {
1251:                        getProperties();
1252:                    }
1253:                    n = properties.get(key);
1254:                }
1255:                if (n != null) {
1256:                    return n;
1257:                } else {
1258:                    return null;
1259:                }
1260:            }
1261:
1262:            /**
1263:             * Sets a specified property for this node.
1264:             * This method does not commit anything - it merely updates the node's propertylist.
1265:             * @param node the property object as a <code>MMObjectNode</code>
1266:             */
1267:            public void putProperty(MMObjectNode node) {
1268:                synchronized (properties_sync) {
1269:                    if (properties == null) {
1270:                        getProperties();
1271:                    }
1272:                    properties.put(node.getStringValue("key"), node);
1273:                }
1274:            }
1275:
1276:            /**
1277:             * Return the GUI indicator for this node.
1278:             * The GUI indicator is a string that represents the contents of this node.
1279:             * By default it is the string-representation of the first non-system field of the node.
1280:             * Individual builders can alter this behavior.
1281:             * @return the GUI iddicator as a <code>String</code>
1282:             */
1283:            public String getGUIIndicator() {
1284:                if (parent != null) {
1285:                    return parent.getGUIIndicator(this );
1286:                } else {
1287:                    log.error("MMObjectNode -> can't get parent");
1288:                    return "problem";
1289:                }
1290:            }
1291:
1292:            /**
1293:             * Return the buildername of this node
1294:             * @return the builder table name
1295:             */
1296:            public String getName() {
1297:                return parent.getTableName();
1298:            }
1299:
1300:            /**
1301:             * Delete the relation cache for this node.
1302:             * This means it will be reloaded from the database/storage on next use.
1303:             */
1304:            public void delRelationsCache() {
1305:                delRelationsCache(getNumber());
1306:            }
1307:
1308:            /**
1309:             * Delete the relation cache for this node.
1310:             * This means it will be reloaded from the database/storage on next use.
1311:             * @param number nodenumber
1312:             */
1313:            public static void delRelationsCache(Integer number) {
1314:                RelationsCache.getCache().remove(number);
1315:            }
1316:
1317:            /**
1318:             * Returns whether this node has relations.
1319:             * This includes unidirection relations which would otherwise not be counted.
1320:             * @return <code>true</code> if any relations exist, <code>false</code> otherwise.
1321:             */
1322:            public boolean hasRelations() {
1323:                // return getRelationCount()>0;
1324:                return parent.mmb.getInsRel().hasRelations(getNumber());
1325:            }
1326:
1327:            /**
1328:             * Return all the relations of this node.
1329:             * Use only to delete the relations of a node.
1330:             * Note that this returns the nodes describing the relation - not the nodes 'related to'.
1331:             * @return An <code>Enumeration</code> containing the nodes
1332:             */
1333:            public Enumeration<MMObjectNode> getAllRelations() {
1334:                Vector<MMObjectNode> allrelations = parent.mmb.getInsRel()
1335:                        .getAllRelationsVector(getNumber());
1336:                if (allrelations != null) {
1337:                    return allrelations.elements();
1338:                } else {
1339:                    return null;
1340:                }
1341:            }
1342:
1343:            /**
1344:             * Return the relations of this node.
1345:             * Note that this returns the nodes describing the relation - not the nodes 'related to'.
1346:             *
1347:             *
1348:             * XXX: return type of this method makes it impossible to make MMObjectNode implements Node, perhaps it needs change
1349:             *
1350:             * @return An <code>Enumeration</code> containing the nodes
1351:             */
1352:            public Enumeration<MMObjectNode> getRelations() {
1353:                List<MMObjectNode> relations = getRelationNodes();
1354:                if (relations != null) {
1355:                    return Collections.enumeration(relations);
1356:                } else {
1357:                    return null;
1358:                }
1359:            }
1360:
1361:            /**
1362:             * @since MMBase-1.7
1363:             * @scope public?
1364:             */
1365:            protected List<MMObjectNode> getRelationNodes() {
1366:                Integer number = Integer.valueOf(getNumber());
1367:                List<MMObjectNode> relations;
1368:                RelationsCache relationsCache = RelationsCache.getCache();
1369:                if (!relationsCache.contains(number)) {
1370:                    relations = parent.getRelations_main(getNumber());
1371:                    relationsCache.put(number, relations);
1372:
1373:                } else {
1374:                    relations = relationsCache.get(number);
1375:                }
1376:                return relations;
1377:            }
1378:
1379:            /**
1380:             * Remove the relations of the node.
1381:             */
1382:            public void removeRelations() {
1383:                parent.removeRelations(this );
1384:            }
1385:
1386:            /**
1387:             * Returns the number of relations of this node.
1388:             * @return An <code>int</code> indicating the number of nodes found
1389:             */
1390:            public int getRelationCount() {
1391:                List<MMObjectNode> relations = getRelationNodes();
1392:                if (relations != null) {
1393:                    return relations.size();
1394:                } else {
1395:                    return 0;
1396:                }
1397:            }
1398:
1399:            /**
1400:             * Return the relations of this node, filtered on a specified type.
1401:             * Note that this returns the nodes describing the relation - not the nodes 'related to'.
1402:             * @param otype the 'type' of relations to return. The type identifies a relation (InsRel-derived) builder, not a reldef object.
1403:             * @return An <code>Enumeration</code> containing the nodes
1404:             */
1405:            public Enumeration<MMObjectNode> getRelations(int otype) {
1406:                Enumeration<MMObjectNode> e = getRelations();
1407:                Vector<MMObjectNode> result = new Vector<MMObjectNode>();
1408:                if (e != null) {
1409:                    while (e.hasMoreElements()) {
1410:                        MMObjectNode tnode = e.nextElement();
1411:                        if (tnode.getOType() == otype) {
1412:                            result.addElement(tnode);
1413:                        }
1414:                    }
1415:                }
1416:                return result.elements();
1417:            }
1418:
1419:            /**
1420:             * Return the relations of this node, filtered on a specified type.
1421:             * Note that this returns the nodes describing the relation - not the nodes 'related to'.
1422:             * @param wantedtype the 'type' of relations to return. The type identifies a relation (InsRel-derived) builder, not a reldef object.
1423:             * @return An <code>Enumeration</code> containing the nodes
1424:             */
1425:            public Enumeration<MMObjectNode> getRelations(String wantedtype) {
1426:                int otype = parent.mmb.getTypeDef().getIntValue(wantedtype);
1427:                if (otype != -1) {
1428:                    return getRelations(otype);
1429:                }
1430:                return null;
1431:            }
1432:
1433:            /**
1434:             * Return the number of relations of this node, filtered on a specified type.
1435:             * @param wt the 'type' of related nodes (NOT the relations!).
1436:             * @return An <code>int</code> indicating the number of nodes found
1437:             */
1438:            public int getRelationCount(String wt) {
1439:                int count = 0;
1440:                MMObjectBuilder wantedType = parent.mmb.getBuilder(wt);
1441:                if (wantedType != null) {
1442:                    List<MMObjectNode> relations = getRelationNodes();
1443:                    if (relations != null) {
1444:                        for (Enumeration<MMObjectNode> e = Collections
1445:                                .enumeration(relations); e.hasMoreElements();) {
1446:                            MMObjectNode tnode = e.nextElement();
1447:                            int relation_number = tnode.getIntValue("snumber");
1448:                            int nodetype = 0;
1449:
1450:                            // bugfix #6432: marcel: determine source of relation, get type, display
1451:                            // error when nodetype is determined to be -1, which is a possible wrongly inserted relation
1452:
1453:                            if (relation_number == getNumber()) {
1454:                                relation_number = tnode.getIntValue("dnumber");
1455:                                nodetype = parent.getNodeType(relation_number);
1456:                            } else {
1457:                                nodetype = parent.getNodeType(relation_number);
1458:                            }
1459:
1460:                            // Display situation where snumber or dnumber from a relation-node does not seem to
1461:                            // exsist in the database. This can be fixed by mannually removing the node out of the insrel-table
1462:                            if (nodetype == -1) {
1463:                                log
1464:                                        .warn("Warning: relation_node("
1465:                                                + tnode.getNumber()
1466:                                                + ") has a possible removed relation_number("
1467:                                                + relation_number
1468:                                                + "), manually check its consistency!");
1469:                            }
1470:
1471:                            MMObjectBuilder nodeType = parent.mmb
1472:                                    .getBuilder(parent.mmb.getTypeDef()
1473:                                            .getValue(nodetype));
1474:                            if (nodeType != null
1475:                                    && (nodeType.equals(wantedType) || nodeType
1476:                                            .isExtensionOf(wantedType))) {
1477:                                count++;
1478:                            }
1479:                        }
1480:                    }
1481:                } else {
1482:                    log
1483:                            .warn("getRelationCount is requested with an invalid Builder name (otype "
1484:                                    + wt + " does not exist)");
1485:                }
1486:                return count;
1487:            }
1488:
1489:            /**
1490:             * Return the age of the node, determined using the daymarks builder.
1491:             * @return the age in days, or 0 if unknown (daymarks builder not present)
1492:             */
1493:            public int getAge() {
1494:                DayMarkers dayMarkers = ((DayMarkers) parent.mmb
1495:                        .getBuilder("daymarks"));
1496:                if (dayMarkers == null)
1497:                    return 0;
1498:                return dayMarkers.getAge(getNumber());
1499:            }
1500:
1501:            /**
1502:             * Sends a field-changed signal.
1503:             * @param fieldName the name of the changed field.
1504:             * @return always <code>true</code>
1505:             */
1506:            public boolean sendFieldChangeSignal(String fieldName) {
1507:                return parent.sendFieldChangeSignal(this , fieldName);
1508:            }
1509:
1510:            /**
1511:             * Sets the node's alias.
1512:             * The code only sets a (memory) property, it does not actually add the alias to the database.
1513:             * Only works for uninserted Nodes. So this is actually only used for application import.
1514:             * No need to use this. Use {@link MMObjectBuilder#createAlias}.
1515:             */
1516:            public void setAlias(String alias) {
1517:                if (aliases == null)
1518:                    aliases = new HashSet<String>();
1519:                synchronized (aliases) {
1520:                    aliases.add(alias);
1521:                }
1522:            }
1523:
1524:            /**
1525:             * Returns the node's alias.
1526:             * Does not support multiple aliases.
1527:             */
1528:            void useAliases() {
1529:                if (aliases != null) {
1530:                    synchronized (aliases) {
1531:                        if (getNumber() <= 0) {
1532:                            log
1533:                                    .error("Trying to set aliases for uncommited node!!");
1534:                            return;
1535:                        }
1536:                        for (String alias : aliases) {
1537:                            try {
1538:                                parent.createAlias(getNumber(), alias,
1539:                                        getStringValue("owner"));
1540:                            } catch (org.mmbase.storage.StorageException se) {
1541:                                log.error(se);
1542:                            }
1543:                        }
1544:                        aliases.clear();
1545:                    }
1546:                }
1547:            }
1548:
1549:            /**
1550:             * Get all related nodes. The returned nodes are not the
1551:             * nodes directly attached to this node (the relation nodes) but the nodes
1552:             * attached to the relation nodes of this node.
1553:             *
1554:             * XXX: return type of this method make it impossible to make MMObjectNode implements Node, perhaps it needs change
1555:             *
1556:             * @return a <code>Vector</code> containing <code>MMObjectNode</code>s
1557:             */
1558:            public Vector<MMObjectNode> getRelatedNodes() {
1559:                return getRelatedNodes("object", null,
1560:                        RelationStep.DIRECTIONS_EITHER);
1561:            }
1562:
1563:            /**
1564:             * Makes number -> MMObjectNode of a list of MMObjectNodes.
1565:             * @since MMBase-1.6.2
1566:             */
1567:            private Map<Integer, MMObjectNode> makeMap(List<MMObjectNode> v) {
1568:                Map<Integer, MMObjectNode> result = new HashMap<Integer, MMObjectNode>();
1569:                for (MMObjectNode node : v) {
1570:                    result.put(node.getNumber(), node);
1571:                }
1572:                return result;
1573:            }
1574:
1575:            /**
1576:             * Get the related nodes of a certain type. The returned nodes are not the
1577:             * nodes directly attached to this node (the relation nodes) but the nodes
1578:             * attached to the relation nodes of this
1579:             *
1580:             * XXX: return type of this method make it impossible to make MMObjectNode implements Node, perhaps it needs change
1581:             *
1582:             * @param type the type of objects to be returned
1583:             * @return a <code>Vector</code> containing <code>MMObjectNode</code>s
1584:             */
1585:            public Vector<MMObjectNode> getRelatedNodes(String type) {
1586:                if (log.isDebugEnabled()) {
1587:                    log.debug("Getting related nodes of " + this  + " of type "
1588:                            + type);
1589:                }
1590:
1591:                if (InsRel.usesdir) {
1592:                    return getRelatedNodes(type, RelationStep.DIRECTIONS_BOTH);
1593:                } else {
1594:                    //
1595:                    // determine related nodes
1596:                    Map<Integer, MMObjectNode> source = makeMap(getRelatedNodes(
1597:                            type, RelationStep.DIRECTIONS_SOURCE));
1598:                    Map<Integer, MMObjectNode> destin = makeMap(getRelatedNodes(
1599:                            type, RelationStep.DIRECTIONS_DESTINATION));
1600:
1601:                    if (log.isDebugEnabled()) {
1602:                        log.debug("source(" + source.size() + ") - destin("
1603:                                + destin.size() + ")");
1604:                    }
1605:                    // remove duplicates (can happen if multirel is being used when no dir on insrel exists)
1606:                    destin.putAll(source);
1607:                    return new Vector<MMObjectNode>(destin.values());
1608:                }
1609:            }
1610:
1611:            /**
1612:             * If you query from this_node_type(type) (typex, insrel, typey where typex == typey) {
1613:             *   if the insrel table is directional, use the multirelations.SEARCH_BOTH
1614:             *   if the insrel table is not directional, use the multirelations.SEARCH_SOURCE + multirelations.SEARCH_DESTINATION
1615:             * }
1616:             * Otherwise the SEARCH_BOTH will result in an OR on insrel which will never return in
1617:             * (huge) databases.
1618:             * @param type the type of teh realted node to return
1619:             * @param search_type the type of directionality to use
1620:             * @since MMBase-1.6.3
1621:             */
1622:            public Vector<MMObjectNode> getRelatedNodes(String type,
1623:                    int search_type) {
1624:                return getRelatedNodes(type, "insrel", search_type);
1625:            }
1626:
1627:            /**
1628:             * If you query from this_node_type(type) (typex, insrel, typey where typex == typey) {
1629:             *   if the insrel table is directional, use the multirelations.SEARCH_BOTH
1630:             *   if the insrel table is not directional, use the multirelations.SEARCH_SOURCE + multirelations.SEARCH_DESTINATION
1631:             * }
1632:             * Otherwise the SEARCH_BOTH will result in an OR on insrel which will never return in
1633:             * (huge) databases.
1634:             * @param type the type of teh realted node to return
1635:             * @param role the role of the relation (null if no role specified)
1636:             * @param search_type the type of directionality to use
1637:             * @since MMBase-1.6.3
1638:             */
1639:            public Vector<MMObjectNode> getRelatedNodes(String type,
1640:                    String role, int search_type) {
1641:                Vector<MMObjectNode> result = null;
1642:
1643:                MMObjectBuilder builder = parent.mmb.getBuilder(type);
1644:
1645:                // example: we want a thisnode.relatedNodes(mediaparts) where mediaparts are of type
1646:                // audioparts and videoparts. This method will return the real nodes (thus of type audio/videoparts)
1647:                // when asked to get nodes of type mediaparts.
1648:                //
1649:                // - get a list of virtual nodes from a multilevel("this.parent.name, type") ordered on otype
1650:                //   (this will return virtual audio- and/or videoparts ordered on their *real* parent)
1651:                // - construct a list of nodes for each parentbuilder seperately
1652:                // - ask the parentbuilder for each list of virtual nodes to get a list of the real nodes
1653:                if (builder != null) {
1654:
1655:                    ClusterBuilder clusterBuilder = parent.mmb
1656:                            .getClusterBuilder();
1657:
1658:                    // multilevel from table this.parent.name -> type
1659:                    List<String> tables = new ArrayList<String>();
1660:                    tables.add(parent.getTableName() + "1");
1661:                    if (role != null) {
1662:                        tables.add(role);
1663:                    }
1664:                    tables.add(type + "2");
1665:
1666:                    // return type.number (and otype for sorting)
1667:                    List<String> fields = new ArrayList<String>();
1668:                    fields.add(type + "2.number");
1669:                    fields.add(type + "2.otype");
1670:
1671:                    // order list UP
1672:                    List<String> directions = new ArrayList<String>();
1673:                    directions.add("UP");
1674:
1675:                    // and order on otype
1676:                    List<String> ordered = new ArrayList<String>();
1677:                    ordered.add(type + "2.otype");
1678:
1679:                    List<String> snodes = new ArrayList<String>();
1680:                    snodes.add("" + getNumber());
1681:
1682:                    SearchQuery query = clusterBuilder
1683:                            .getMultiLevelSearchQuery(snodes, fields, "NO",
1684:                                    tables, null, ordered, directions,
1685:                                    search_type);
1686:                    RelatedNodesCache relatedCache = RelatedNodesCache
1687:                            .getCache();
1688:                    List<MMObjectNode> v = relatedCache.get(query);
1689:                    if (v == null) {
1690:                        try {
1691:                            v = clusterBuilder.getClusterNodes(query);
1692:                            relatedCache.put(query, v);
1693:                        } catch (SearchQueryException sqe) {
1694:                            log.error(sqe.toString());
1695:                            v = null;
1696:                        }
1697:                    }
1698:                    if (v == null) {
1699:                        result = new Vector<MMObjectNode>();
1700:                    } else {
1701:                        result = new Vector<MMObjectNode>(getRealNodes(v, type
1702:                                + "2"));
1703:                    }
1704:                } else {
1705:                    log.error("This type(" + type
1706:                            + ") is not a valid buildername!");
1707:                    result = new Vector<MMObjectNode>(); // return empty vector
1708:                }
1709:
1710:                if (log.isDebugEnabled()) {
1711:                    log.debug("related(" + parent.getTableName() + "("
1712:                            + getNumber() + ")) -> " + type + " = size("
1713:                            + result.size() + ")");
1714:                }
1715:
1716:                return result;
1717:            }
1718:
1719:            /**
1720:             * Loop through the virtuals vector, group all same nodes based on parent and fetch the real nodes from those parents
1721:             *
1722:             * @param List  of virtual nodes (only type.number and type.otype fields are set)
1723:             * @param type, needed to retreive the otype, which is set in node as type + ".otype"
1724:             * @returns List of real nodes
1725:             *
1726:             * @see getRelatedNodes(String type)
1727:             * @since MMBase-1.6.2
1728:             */
1729:            private List<MMObjectNode> getRealNodes(
1730:                    List<MMObjectNode> virtuals, String type) {
1731:
1732:                log.debug("Getting real nodes");
1733:                List<MMObjectNode> result = new ArrayList<MMObjectNode>();
1734:
1735:                List<MMObjectNode> list = new ArrayList<MMObjectNode>();
1736:                int ootype = -1;
1737:                List<Integer> virtualNumbers = new ArrayList<Integer>();
1738:
1739:                // fill the list
1740:                Iterator<MMObjectNode> i = virtuals.iterator();
1741:                while (i.hasNext()) {
1742:                    MMObjectNode node = i.next();
1743:                    Integer number = node.getIntegerValue(type + ".number");
1744:                    if (!virtualNumbers.contains(number)) {
1745:                        virtualNumbers.add(number);
1746:
1747:                        int otype = node.getIntValue(type + ".otype");
1748:
1749:                        // convert the nodes of type ootype to real numbers
1750:                        if (otype != ootype) {
1751:                            // if we have nodes return real values
1752:                            if (ootype != -1) {
1753:                                result.addAll(getRealNodesFromBuilder(list,
1754:                                        ootype));
1755:                                list = new ArrayList<MMObjectNode>();
1756:                            }
1757:                            ootype = otype;
1758:                        }
1759:                        // convert current node type.number and type.otype to number and otype
1760:                        String builderName = parent.mmb.getTypeDef().getValue(
1761:                                otype);
1762:                        if (builderName == null) {
1763:                            log.warn("Could not find builder of node "
1764:                                    + node.getNumber() + " taking 'object'");
1765:                            builderName = "object";
1766:                            otype = parent.mmb.getBuilder(builderName)
1767:                                    .getObjectType();
1768:                        }
1769:
1770:                        MMObjectNode convert = new MMObjectNode(parent.mmb
1771:                                .getBuilder(builderName), false);
1772:                        // parent needs to be set or else mmbase does nag nag nag on a setValue()
1773:                        convert.setValue(MMObjectBuilder.FIELD_NUMBER, node
1774:                                .getValue(type + ".number"));
1775:                        convert.setValue(MMObjectBuilder.FIELD_OBJECT_TYPE,
1776:                                ootype);
1777:                        list.add(convert);
1778:                    }
1779:                    // first and only list or last list, return real values
1780:                    if (!i.hasNext()) {
1781:                        // log.debug("subconverting last "+list.size()+" nodes of type("+otype+")");
1782:                        result.addAll(getRealNodesFromBuilder(list, ootype));
1783:                    }
1784:                }
1785:
1786:                // check that we didnt loose any nodes
1787:                if (virtualNumbers.size() != result.size()) {
1788:                    log
1789:                            .error("We lost a few nodes during conversion from virtualnodes("
1790:                                    + virtuals.size()
1791:                                    + ") to realnodes("
1792:                                    + result.size() + ")");
1793:                    StringBuffer vNumbers = new StringBuffer();
1794:                    for (int j = 0; j < virtualNumbers.size(); j++) {
1795:                        vNumbers.append(virtualNumbers.get(j)).append(" ");
1796:                    }
1797:                    log.error("Virtual node numbers: " + vNumbers.toString());
1798:                    StringBuffer rNumbers = new StringBuffer();
1799:                    for (int j = 0; j < result.size(); j++) {
1800:                        int resultNumber = (result.get(j))
1801:                                .getIntValue("number");
1802:                        rNumbers.append(resultNumber).append(" ");
1803:                    }
1804:                    log.error("Real node numbers: " + rNumbers.toString());
1805:                }
1806:
1807:                return result;
1808:            }
1809:
1810:            /**
1811:             * Upgrade a certain list of MMObectNodes to the right type.
1812:             * @since MMBase-1.6.2
1813:             */
1814:            private List<MMObjectNode> getRealNodesFromBuilder(
1815:                    List<MMObjectNode> list, int otype) {
1816:                List<MMObjectNode> result = new ArrayList<MMObjectNode>();
1817:                String name = parent.mmb.getTypeDef().getValue(otype);
1818:                if (name != null) {
1819:                    MMObjectBuilder rparent = parent.mmb.getBuilder(name);
1820:                    if (rparent != null) {
1821:                        result.addAll(rparent.getNodes(list));
1822:                    } else {
1823:                        log.error("This otype(" + otype
1824:                                + ") does not denote a valid typedef-name("
1825:                                + name + ")!");
1826:                    }
1827:                } else {
1828:                    log.error("This otype(" + otype
1829:                            + ") gives no name from typedef!");
1830:                }
1831:                return result;
1832:            }
1833:
1834:            public int getByteSize() {
1835:                return getByteSize(new SizeOf());
1836:            }
1837:
1838:            public int getByteSize(SizeOf sizeof) {
1839:                return sizeof.sizeof(values) + sizeof.sizeof(oldValues)
1840:                        + sizeof.sizeof(sizes) + sizeof.sizeof(properties)
1841:                        + sizeof.sizeof(changed) + 12 * SizeOf.SZ_REF;
1842:            }
1843:
1844:            /**
1845:             * @since MMBase-1.6.2
1846:             */
1847:            public int hashCode() {
1848:                if (parent != null) {
1849:                    return parent.hashCode(this );
1850:                } else {
1851:                    return super .hashCode();
1852:                }
1853:            }
1854:
1855:            /**
1856:             * @since MMBase-1.6.2
1857:             */
1858:            public boolean equals(Object o) {
1859:                if (o instanceof  MMObjectNode) {
1860:                    MMObjectNode n = (MMObjectNode) o;
1861:                    if (parent != null) {
1862:                        return parent.equals(this , n);
1863:                    } else {
1864:                        return defaultEquals(n);
1865:                    }
1866:                }
1867:                return false;
1868:            }
1869:
1870:            /**
1871:             * @since MMBase-1.6.2
1872:             */
1873:            public boolean defaultEquals(MMObjectNode n) {
1874:                /*
1875:                  if (getNumber() >= 0) {  // we know when real nodes are equal
1876:                  return n.getNumber() == getNumber();
1877:                  } else { // I don't know about others
1878:                  return super.equals(n); // compare as objects.
1879:                  }
1880:                 */
1881:                return super .equals(n); // compare as objects.
1882:            }
1883:
1884:            /**
1885:             * Custom serialize method for MMObjectNode. The main reason this method exists is
1886:             * that the builder for an object will not be serialized, but the tablename for
1887:             * the object will be saved instead. During deserialization the builder will
1888:             * be recovered using that name.
1889:             * @since MMBase-1.8.0
1890:             */
1891:            private void writeObject(java.io.ObjectOutputStream out)
1892:                    throws IOException {
1893:                out.writeObject(oldValues);
1894:                out.writeObject(values);
1895:                out.writeObject(sizes);
1896:                out.writeBoolean(initializing);
1897:                out.writeObject(properties);
1898:                out.writeObject(changed);
1899:
1900:                // Save parent and builder by name, not by object
1901:                if (parent == null) {
1902:                    out.writeObject(null);
1903:                } else {
1904:                    out.writeObject(parent.getTableName());
1905:                }
1906:                if (builder == null) {
1907:                    out.writeObject(null);
1908:                } else {
1909:                    out.writeObject(builder.getTableName());
1910:                }
1911:                out.writeBoolean(isNew);
1912:                out.writeObject(aliases);
1913:                out.writeObject(newContext);
1914:            }
1915:
1916:            /**
1917:             * Custom deserialize method for MMObjectNode. The main reason this method exists is
1918:             * that the builder for an object will not be serialized, but the tablename for
1919:             * the object will be saved instead. During deserialization the builder will
1920:             * be recovered using that name.
1921:             * @since MMBase-1.8.0
1922:             */
1923:            @SuppressWarnings("unchecked")
1924:            private void readObject(java.io.ObjectInputStream in)
1925:                    throws IOException, ClassNotFoundException {
1926:                oldValues = (Map<String, Object>) in.readObject();
1927:                values = (Map<String, Object>) in.readObject();
1928:                sizes = (Map<String, Long>) in.readObject();
1929:                initializing = in.readBoolean();
1930:                properties = (Hashtable<String, MMObjectNode>) in.readObject();
1931:                changed = (Set<String>) in.readObject();
1932:
1933:                // Retrieve parent and builder by name, not by object
1934:                String parentName = (String) in.readObject();
1935:                if (parentName != null) {
1936:                    parent = MMBase.getMMBase().getBuilder(parentName);
1937:                }
1938:                String builderName = (String) in.readObject();
1939:                if (builderName != null) {
1940:                    builder = MMBase.getMMBase().getBuilder(builderName);
1941:                }
1942:                isNew = in.readBoolean();
1943:                aliases = (Set<String>) in.readObject();
1944:                newContext = (String) in.readObject();
1945:            }
1946:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.