Source Code Cross Referenced for BasicDataType.java in  » Database-ORM » MMBase » org » mmbase » datatypes » 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.datatypes 
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:
0011:        package org.mmbase.datatypes;
0012:
0013:        import java.io.*;
0014:        import java.util.*;
0015:
0016:        import org.mmbase.bridge.*;
0017:        import org.mmbase.bridge.util.Queries;
0018:        import org.mmbase.core.AbstractDescriptor;
0019:        import org.mmbase.core.util.Fields;
0020:        import org.mmbase.datatypes.processors.*;
0021:        import org.mmbase.security.Rank;
0022:        import org.mmbase.storage.search.Constraint;
0023:        import org.mmbase.storage.search.FieldCompareConstraint;
0024:        import org.mmbase.util.*;
0025:        import org.mmbase.util.logging.Logger;
0026:        import org.mmbase.util.logging.Logging;
0027:        import org.mmbase.util.xml.DocumentReader;
0028:        import org.w3c.dom.Element;
0029:
0030:        /**
0031:         * Every DataType extends this one. It's extensions can however implement several extensions of the
0032:         * DataType interface (e.g. some datatypes (at least {@link StringDataType}) are both {@link LengthDataType}
0033:         * and {@link ComparableDataType}, and some are only one ({@link BinaryDataType}, {@link
0034:         * NumberDataType}). In other words, this arrangement is like this, because java does not support
0035:         * Multipible inheritance.
0036:         *
0037:         * @author Pierre van Rooden
0038:         * @author Michiel Meeuwissen
0039:         * @since  MMBase-1.8
0040:         * @version $Id: BasicDataType.java,v 1.83 2008/02/16 22:13:53 nklasens Exp $
0041:         */
0042:
0043:        public class BasicDataType<C> extends AbstractDescriptor implements 
0044:                DataType<C>, Cloneable, Comparable<DataType<C>>, Descriptor {
0045:            /**
0046:             * The bundle used by datatype to determine default prompts for error messages when a
0047:             * validation fails.
0048:             */
0049:            public static final String DATATYPE_BUNDLE = "org.mmbase.datatypes.resources.datatypes";
0050:            private static final Logger log = Logging
0051:                    .getLoggerInstance(BasicDataType.class);
0052:
0053:            protected RequiredRestriction requiredRestriction = new RequiredRestriction(
0054:                    false);
0055:            protected UniqueRestriction uniqueRestriction = new UniqueRestriction(
0056:                    false);
0057:            protected TypeRestriction typeRestriction = new TypeRestriction();
0058:            protected EnumerationRestriction enumerationRestriction = new EnumerationRestriction(
0059:                    (LocalizedEntryListFactory<C>) null);
0060:
0061:            /**
0062:             * The datatype from which this datatype originally inherited it's properties.
0063:             */
0064:            protected BasicDataType<?> origin = null;
0065:
0066:            private Object owner;
0067:            private Class<C> classType;
0068:            private C defaultValue;
0069:
0070:            private CommitProcessor commitProcessor = EmptyCommitProcessor
0071:                    .getInstance();
0072:            private Processor[] getProcessors;
0073:            private Processor[] setProcessors;
0074:
0075:            private Element xml = null;
0076:
0077:            /**
0078:             * Create a data type object of unspecified class type
0079:             * @param name the name of the data types
0080:             */
0081:            public BasicDataType(String name) {
0082:                this (name, (Class<C>) Object.class);
0083:            }
0084:
0085:            /**
0086:             * Create a data type object
0087:             * @param name the name of the data type
0088:             * @param classType the class of the data type's possible value
0089:             */
0090:            protected BasicDataType(String name, Class<C> classType) {
0091:                super (name);
0092:                this .classType = classType;
0093:                owner = null;
0094:            }
0095:
0096:            private static final long serialVersionUID = 1L; // increase this if object serialization changes (which we shouldn't do!)
0097:
0098:            // implementation of serializable
0099:            private void writeObject(ObjectOutputStream out) throws IOException {
0100:                out.writeUTF(key);
0101:                out.writeObject(description);
0102:                out.writeObject(guiName);
0103:                out.writeObject(requiredRestriction);
0104:                out.writeObject(uniqueRestriction);
0105:                out.writeObject(enumerationRestriction.getEnumerationFactory());
0106:                if (owner instanceof  Serializable) {
0107:                    out.writeObject(owner);
0108:                } else {
0109:                    out.writeObject(owner == null ? null : "OWNER");
0110:                }
0111:                out.writeObject(classType);
0112:                if (defaultValue instanceof  Serializable
0113:                        || defaultValue == null) {
0114:                    out.writeObject(defaultValue);
0115:                } else {
0116:                    log
0117:                            .warn("Default value "
0118:                                    + defaultValue.getClass()
0119:                                    + " '"
0120:                                    + defaultValue
0121:                                    + "' is not serializable, taking it null, which may not be correct.");
0122:                    out.writeObject(null);
0123:                }
0124:                out.writeObject(commitProcessor);
0125:                out.writeObject(getProcessors);
0126:                out.writeObject(setProcessors);
0127:            }
0128:
0129:            // implementation of serializable
0130:            private void readObject(ObjectInputStream in) throws IOException,
0131:                    ClassNotFoundException {
0132:                key = in.readUTF();
0133:                description = (LocalizedString) in.readObject();
0134:                guiName = (LocalizedString) in.readObject();
0135:                requiredRestriction = (RequiredRestriction) in.readObject();
0136:                uniqueRestriction = (UniqueRestriction) in.readObject();
0137:                enumerationRestriction = new EnumerationRestriction(
0138:                        (LocalizedEntryListFactory<C>) in.readObject());
0139:                typeRestriction = new TypeRestriction(); // its always the same, so no need actually persisting it.
0140:                owner = in.readObject();
0141:                try {
0142:                    classType = (Class<C>) in.readObject();
0143:                } catch (Throwable t) {
0144:                    // if some unknown class, simply fall back
0145:                    classType = (Class<C>) Object.class;
0146:                }
0147:                defaultValue = (C) in.readObject();
0148:                commitProcessor = (CommitProcessor) in.readObject();
0149:                getProcessors = (Processor[]) in.readObject();
0150:                setProcessors = (Processor[]) in.readObject();
0151:            }
0152:
0153:            public String getBaseTypeIdentifier() {
0154:                return Fields.getTypeDescription(getBaseType()).toLowerCase();
0155:            }
0156:
0157:            public int getBaseType() {
0158:                return Fields.classToType(classType);
0159:            }
0160:
0161:            /**
0162:             * {@inheritDoc}
0163:             * Calls both {@link #inheritProperties} and {@link #inheritRestrictions}.
0164:             */
0165:            public final void inherit(BasicDataType<C> origin) {
0166:                edit();
0167:                inheritProperties(origin);
0168:                inheritRestrictions(origin);
0169:            }
0170:
0171:            /**
0172:             * Properties are members of the datatype that can easily be copied/clones.
0173:             */
0174:            protected void inheritProperties(BasicDataType<C> origin) {
0175:                this .origin = origin;
0176:
0177:                defaultValue = origin.getDefaultValue();
0178:
0179:                commitProcessor = (CommitProcessor) (origin.commitProcessor instanceof  PublicCloneable ? ((PublicCloneable) origin.commitProcessor)
0180:                        .clone()
0181:                        : origin.commitProcessor);
0182:                if (origin.getProcessors == null) {
0183:                    getProcessors = null;
0184:                } else {
0185:                    getProcessors = origin.getProcessors.clone();
0186:                }
0187:                if (origin.setProcessors == null) {
0188:                    setProcessors = null;
0189:                } else {
0190:                    setProcessors = origin.setProcessors.clone();
0191:                }
0192:            }
0193:
0194:            /**
0195:             * If a datatype is cloned, the restrictions of it (normally implemented as inner classes), must be reinstantiated.
0196:             */
0197:            protected void cloneRestrictions(BasicDataType<C> origin) {
0198:                enumerationRestriction = new EnumerationRestriction(
0199:                        origin.enumerationRestriction);
0200:                requiredRestriction = new RequiredRestriction(
0201:                        origin.requiredRestriction);
0202:                uniqueRestriction = new UniqueRestriction(
0203:                        origin.uniqueRestriction);
0204:            }
0205:
0206:            /**
0207:             * If a datatype inherits from another datatype all its restrictions inherit too.
0208:             */
0209:            protected void inheritRestrictions(BasicDataType<C> origin) {
0210:                if (!origin.getEnumerationFactory().isEmpty()) {
0211:                    enumerationRestriction
0212:                            .inherit(origin.enumerationRestriction);
0213:                    if (enumerationRestriction.value != null) {
0214:                        LocalizedEntryListFactory<C> fact = enumerationRestriction
0215:                                .getEnumerationFactory();
0216:                        if (!origin.getTypeAsClass().equals(getTypeAsClass())) {
0217:                            // Reevaluate XML configuration, because it was done with a 'wrong' suggestion for the wrapper class.
0218:                            Element elm = fact.toXml();
0219:                            if (elm == null) {
0220:                                log
0221:                                        .warn("Did not get XML from Factory "
0222:                                                + fact);
0223:                            } else {
0224:                                // need to clone the actual factory,
0225:                                // since it will otherwise change the original restrictions.
0226:                                fact = new LocalizedEntryListFactory<C>();
0227:                                fact.fillFromXml(elm, getTypeAsClass());
0228:                                enumerationRestriction.setValue(fact);
0229:                            }
0230:                        }
0231:                    }
0232:                }
0233:
0234:                requiredRestriction.inherit(origin.requiredRestriction);
0235:                uniqueRestriction.inherit(origin.uniqueRestriction);
0236:            }
0237:
0238:            /**
0239:             * {@inheritDoc}
0240:             */
0241:            public BasicDataType<?> getOrigin() {
0242:                return origin;
0243:            }
0244:
0245:            /**
0246:             * {@inheritDoc}
0247:             */
0248:            public Class<C> getTypeAsClass() {
0249:                return classType;
0250:            }
0251:
0252:            /**
0253:             * Checks if the passed object is of the correct class (compatible with the type of this DataType),
0254:             * and throws an IllegalArgumentException if it doesn't.
0255:             * @param value teh value whose type (class) to check
0256:             * @throws IllegalArgumentException if the type is not compatible
0257:             */
0258:            protected boolean isCorrectType(Object value) {
0259:                return (value == null && !isRequired())
0260:                        || Casting.isType(classType, value);
0261:            }
0262:
0263:            /**
0264:             * {@inheritDoc}
0265:             */
0266:            public void checkType(Object value) {
0267:                if (!isCorrectType(value)) {
0268:                    // customize this?
0269:                    throw new IllegalArgumentException("DataType of '" + value
0270:                            + "' for '" + getName() + "' must be of type "
0271:                            + classType + " (but is "
0272:                            + (value == null ? value : value.getClass()) + ")");
0273:                }
0274:            }
0275:
0276:            /**
0277:             * {@inheritDoc}
0278:             *
0279:             * Tries to determin  cloud by node and field if possible and wraps {@link #preCast(Object, Cloud, Node, Field)}.
0280:             */
0281:            public final <D> D preCast(D value, Node node, Field field) {
0282:                //public final Object preCast(Object value, Node node, Field field) {
0283:                return preCast(value, getCloud(node, field), node, field);
0284:            }
0285:
0286:            /**
0287:             * This method is as yet unused, but can be anticipated
0288:             */
0289:            //public final <D> D preCast(D value, Cloud cloud) {
0290:            public final Object preCast(Object value, Cloud cloud) {
0291:                return preCast(value, cloud, null, null);
0292:            }
0293:
0294:            /**
0295:             * This method implements 'precasting', which can be seen as a kind of datatype specific
0296:             * casting.  It should anticipate that every argument can be <code>null</code>. It should not
0297:             * change the actual type of the value.
0298:             */
0299:            protected <D> D preCast(D value, Cloud cloud, Node node, Field field) {
0300:                if (value == null)
0301:                    return null;
0302:                D preCast = enumerationRestriction.preCast(value, cloud);
0303:                return preCast;
0304:            }
0305:
0306:            /**
0307:             * {@inheritDoc}
0308:             *
0309:             * No need to override this. It is garantueed by javadoc that cast should work out of preCast
0310:             * using Casting.toType. So that is what this final implementation is doing.
0311:             *
0312:             * Override {@link #preCast(Object, Cloud, Node, Field)}
0313:             */
0314:            public final C cast(Object value, final Node node, final Field field) {
0315:                if (origin != null
0316:                        && (!origin.getClass().isAssignableFrom(getClass()))) {
0317:                    // if inherited from incompatible type, then first try to cast in the way of origin.
0318:                    // e.g. if origin is Date, but actual type is integer, then casting of 'today' works now.
0319:                    value = origin.cast(value, node, field);
0320:                }
0321:                Cloud cloud = getCloud(node, field);
0322:                try {
0323:                    return cast(value, cloud, node, field);
0324:                } catch (CastException ce) {
0325:                    log.error(ce);
0326:                    return Casting.toType(classType, cloud, preCast(value,
0327:                            cloud, node, field));
0328:                }
0329:            }
0330:
0331:            /**
0332:             * Utility to avoid repetitive calling of getCloud
0333:             */
0334:            protected C cast(Object value, Cloud cloud, Node node, Field field)
0335:                    throws CastException {
0336:                Object preCast = preCast(value, cloud, node, field);
0337:                if (preCast == null)
0338:                    return null;
0339:                C cast = Casting.toType(classType, cloud, preCast);
0340:                return cast;
0341:            }
0342:
0343:            protected final Cloud getCloud(Node node, Field field) {
0344:                if (node != null)
0345:                    return node.getCloud();
0346:                if (field != null)
0347:                    return field.getNodeManager().getCloud();
0348:                return null;
0349:            }
0350:
0351:            /**
0352:             * Before validating the value, the value will be 'cast', on default this will be to the
0353:             * 'correct' type, but it can be a more generic type sometimes. E.g. for numbers this wil simply
0354:             * cast to Number.
0355:             */
0356:            protected Object castToValidate(Object value, Node node, Field field)
0357:                    throws CastException {
0358:                return cast(value, getCloud(node, field), node, field);
0359:            }
0360:
0361:            /**
0362:             * {@inheritDoc}
0363:             */
0364:            public C getDefaultValue() {
0365:                if (defaultValue == null)
0366:                    return null;
0367:                return cast(defaultValue, null, null);
0368:            }
0369:
0370:            /**
0371:             * {@inheritDoc}
0372:             */
0373:            public void setDefaultValue(C def) {
0374:                edit();
0375:                defaultValue = def;
0376:            }
0377:
0378:            protected Element getElement(Element parent, String name,
0379:                    String path) {
0380:                return getElement(parent, name, name, path);
0381:            }
0382:
0383:            protected Element getElement(Element parent, String pattern,
0384:                    String name, String path) {
0385:                java.util.regex.Pattern p = java.util.regex.Pattern
0386:                        .compile("\\A" + pattern + "\\z");
0387:                org.w3c.dom.NodeList nl = parent.getChildNodes();
0388:                Element el = null;
0389:                for (int i = 0; i < nl.getLength(); i++) {
0390:                    org.w3c.dom.Node child = nl.item(i);
0391:                    if (child instanceof  Element) {
0392:                        if (p.matcher(child.getLocalName()).matches()) {
0393:                            el = (Element) child;
0394:                            break;
0395:                        }
0396:                    }
0397:                }
0398:                if (el == null) {
0399:                    el = parent.getOwnerDocument().createElementNS(XMLNS, name);
0400:                    DocumentReader.appendChild(parent, el, path);
0401:                } else if (!el.getLocalName().equals(name)) {
0402:                    Element newChild = parent.getOwnerDocument()
0403:                            .createElementNS(XMLNS, name);
0404:                    parent.replaceChild(newChild, el);
0405:                    el = newChild;
0406:                }
0407:                return el;
0408:            }
0409:
0410:            protected String getEnforceString(int enforce) {
0411:                switch (enforce) {
0412:                case DataType.ENFORCE_ABSOLUTE:
0413:                    return "absolute";
0414:                case DataType.ENFORCE_ALWAYS:
0415:                    return "always";
0416:                case DataType.ENFORCE_ONCHANGE:
0417:                    return "onchange";
0418:                case DataType.ENFORCE_ONCREATE:
0419:                    return "oncreate";
0420:                case DataType.ENFORCE_NEVER:
0421:                    return "never";
0422:                default:
0423:                    return "???";
0424:                }
0425:            }
0426:
0427:            protected Element addRestriction(Element parent, String name,
0428:                    String path, Restriction restriction) {
0429:                return addRestriction(parent, name, name, path, restriction);
0430:            }
0431:
0432:            protected Element addRestriction(Element parent, String pattern,
0433:                    String name, String path, Restriction restriction) {
0434:                Element el = addErrorDescription(getElement(parent, pattern,
0435:                        name, path), restriction);
0436:                xmlValue(el, restriction.getValue());
0437:                el.setAttribute("enforce", getEnforceString(restriction
0438:                        .getEnforceStrength()));
0439:                return el;
0440:            }
0441:
0442:            protected Element addErrorDescription(Element el, Restriction r) {
0443:                r.getErrorDescription().toXml("description", DataType.XMLNS,
0444:                        el, "");
0445:                return el;
0446:            }
0447:
0448:            public boolean isFinished() {
0449:                return owner != null;
0450:            }
0451:
0452:            /**
0453:             * {@inheritDoc}
0454:             */
0455:            public void finish() {
0456:                finish(new Object());
0457:            }
0458:
0459:            /**
0460:             * {@inheritDoc}
0461:             */
0462:            public void finish(Object owner) {
0463:                this .owner = owner;
0464:            }
0465:
0466:            /**
0467:             * {@inheritDoc}
0468:             */
0469:            public DataType<C> rewrite(Object owner) {
0470:                if (this .owner != null) {
0471:                    if (this .owner != owner) {
0472:                        throw new IllegalArgumentException(
0473:                                "Cannot rewrite this datatype - specified owner is not correct");
0474:                    }
0475:                    this .owner = null;
0476:                }
0477:                return this ;
0478:            }
0479:
0480:            /**
0481:             * @javadoc
0482:             */
0483:            protected void edit() {
0484:                if (isFinished()) {
0485:                    throw new IllegalStateException("This data type '"
0486:                            + getName()
0487:                            + "' is finished and can no longer be changed.");
0488:                }
0489:            }
0490:
0491:            /**
0492:             * {@inheritDoc}
0493:             */
0494:            public final Collection<LocalizedString> validate(C value) {
0495:                return validate(value, null, null);
0496:            }
0497:
0498:            public final Collection<LocalizedString> validate(final C value,
0499:                    final Node node, final Field field) {
0500:                return validate(value, node, field, true);
0501:            }
0502:
0503:            /**
0504:             * {@inheritDoc}
0505:             */
0506:            private final Collection<LocalizedString> validate(
0507:                    final Object value, final Node node, final Field field,
0508:                    boolean testEnum) {
0509:                Collection<LocalizedString> errors = VALID;
0510:                Object castValue;
0511:                try {
0512:                    castValue = castToValidate(value, node, field);
0513:                    errors = typeRestriction.validate(errors, castValue, node,
0514:                            field);
0515:                } catch (CastException ce) {
0516:                    log.debug(ce);
0517:                    errors = typeRestriction.addError(errors, value, node,
0518:                            field);
0519:                    castValue = value;
0520:                }
0521:
0522:                if (errors.size() > 0) {
0523:                    // no need continuing, restrictions will probably not know how to handle this value any way.
0524:                    return errors;
0525:                }
0526:
0527:                errors = requiredRestriction.validate(errors, value, node,
0528:                        field);
0529:
0530:                errors = validateCastValueOrNull(errors, castValue, value,
0531:                        node, field);
0532:
0533:                if (castValue == null) {
0534:                    return errors; // null is valid, unless required.
0535:                }
0536:                if (testEnum) {
0537:                    errors = enumerationRestriction.validate(errors, value,
0538:                            node, field);
0539:                }
0540:                errors = uniqueRestriction.validate(errors, castValue, node,
0541:                        field);
0542:                errors = validateCastValue(errors, castValue, value, node,
0543:                        field);
0544:                return errors;
0545:            }
0546:
0547:            public int getEnforceStrength() {
0548:                int enforceStrength = Math.max(typeRestriction
0549:                        .getEnforceStrength(), requiredRestriction
0550:                        .getEnforceStrength());
0551:                enforceStrength = Math.max(enforceStrength,
0552:                        enumerationRestriction.getEnforceStrength());
0553:                return Math.max(enforceStrength, uniqueRestriction
0554:                        .getEnforceStrength());
0555:            }
0556:
0557:            protected Collection<LocalizedString> validateCastValue(
0558:                    Collection<LocalizedString> errors, Object castValue,
0559:                    Object value, Node node, Field field) {
0560:                return errors;
0561:            }
0562:
0563:            /**
0564:             * @since MMBase-1.8.4
0565:             */
0566:            protected Collection<LocalizedString> validateCastValueOrNull(
0567:                    Collection<LocalizedString> errors, Object castValue,
0568:                    Object value, Node node, Field field) {
0569:                return errors;
0570:            }
0571:
0572:            protected StringBuilder toStringBuilder() {
0573:                StringBuilder buf = new StringBuilder();
0574:                buf.append(getName() + " (" + getTypeAsClass()
0575:                        + (defaultValue != null ? ":" + defaultValue : "")
0576:                        + ")");
0577:                buf
0578:                        .append(commitProcessor == null
0579:                                || EmptyCommitProcessor.getInstance() == commitProcessor ? ""
0580:                                : " commit: " + commitProcessor + "");
0581:                if (getProcessors != null) {
0582:                    for (int i = 0; i < 13; i++) {
0583:                        buf.append(getProcessors[i] == null ? "" : ("; get ["
0584:                                + Fields.typeToClass(i) + "]:"
0585:                                + getProcessors[i] + " "));
0586:                    }
0587:                }
0588:                if (setProcessors != null) {
0589:                    for (int i = 0; i < 13; i++) {
0590:                        buf.append(setProcessors[i] == null ? "" : ("; set ["
0591:                                + Fields.typeToClass(i) + "]:"
0592:                                + setProcessors[i] + " "));
0593:                    }
0594:                }
0595:                if (isRequired()) {
0596:                    buf.append("  required");
0597:                }
0598:                if (isUnique()) {
0599:                    buf.append("  unique");
0600:                }
0601:                if (enumerationRestriction.getValue() != null
0602:                        && !enumerationRestriction.getEnumerationFactory()
0603:                                .isEmpty()) {
0604:                    buf.append(" " + enumerationRestriction);
0605:                }
0606:                return buf;
0607:
0608:            }
0609:
0610:            public final String toString() {
0611:                StringBuilder buf = toStringBuilder();
0612:                if (isFinished()) {
0613:                    buf.append(".");
0614:                }
0615:                return buf.toString();
0616:            }
0617:
0618:            /**
0619:             * {@inheritDoc}
0620:             *
0621:             * This method is final, override {@link #clone(String)} in stead.
0622:             */
0623:            public final Object clone() {
0624:                return clone(null);
0625:            }
0626:
0627:            /**
0628:             * {@inheritDoc}
0629:             *
0630:             * Besides super.clone, it calls {@link #inheritProperties(BasicDataType)} and {@link
0631:             * #cloneRestrictions(BasicDataType)}. A clone is not finished. See {@link #isFinished()}.
0632:             */
0633:            public DataType clone(String name) {
0634:                try {
0635:                    BasicDataType<C> clone = (BasicDataType<C>) super 
0636:                            .clone(name);
0637:                    // reset owner if it was set, so this datatype can be changed
0638:                    clone.owner = null;
0639:                    // properly inherit from this datatype (this also clones properties and processor arrays)
0640:                    clone.inheritProperties(this );
0641:                    clone.cloneRestrictions(this );
0642:                    if (log.isTraceEnabled()) {
0643:                        log.trace("Cloned " + this  + " -> " + clone);
0644:                    }
0645:                    return clone;
0646:                } catch (CloneNotSupportedException cnse) {
0647:                    // should not happen
0648:                    log.error("Cannot clone this DataType: " + name);
0649:                    throw new RuntimeException("Cannot clone this DataType: "
0650:                            + name, cnse);
0651:                }
0652:            }
0653:
0654:            public Element toXml() {
0655:                if (xml == null) {
0656:                    xml = DocumentReader.getDocumentBuilder().newDocument()
0657:                            .createElementNS(XMLNS, "datatype");
0658:                    xml.getOwnerDocument().appendChild(xml);
0659:                }
0660:                return xml;
0661:            }
0662:
0663:            public void setXml(Element element) {
0664:                xml = DocumentReader.toDocument(element).getDocumentElement();
0665:                if (origin != null) {
0666:                    xml.setAttribute("base", origin.getName());
0667:                }
0668:                // remove 'specialization' childs (they don't say anything about this datatype itself)
0669:                org.w3c.dom.Node child = xml.getFirstChild();
0670:                while (child != null) {
0671:                    org.w3c.dom.Node next = child.getNextSibling();
0672:                    switch (child.getNodeType()) {
0673:                    case org.w3c.dom.Node.ELEMENT_NODE:
0674:                        if (child.getLocalName().equals("specialization")
0675:                                || child.getLocalName().equals("datatype")) {
0676:                            // fall through and remove
0677:                        } else {
0678:                            break;
0679:                        }
0680:                    case org.w3c.dom.Node.TEXT_NODE:
0681:                        xml.removeChild(child);
0682:                    }
0683:                    child = next;
0684:
0685:                }
0686:            }
0687:
0688:            protected void xmlValue(Element el, Object value) {
0689:                el.setAttribute("value", Casting.toString(value));
0690:            }
0691:
0692:            /**
0693:
0694:             */
0695:            public void toXml(Element parent) {
0696:                parent.setAttribute("id", getName());
0697:
0698:                description.toXml("description", XMLNS, parent, "description");
0699:
0700:                {
0701:                    Element classElement = getElement(parent, "class",
0702:                            "description,class");
0703:                    classElement.setAttribute("name", getClass().getName());
0704:
0705:                    StringBuilder extend = new StringBuilder();
0706:                    Class<?> sup = getClass().getSuperclass();
0707:                    while (DataType.class.isAssignableFrom(sup)) {
0708:                        if (extend.length() > 0)
0709:                            extend.append(',');
0710:                        extend.append(sup.getName());
0711:                        sup = sup.getSuperclass();
0712:                    }
0713:                    for (Class<?> c : getClass().getInterfaces()) {
0714:                        if (DataType.class.isAssignableFrom(c)) {
0715:                            if (extend.length() > 0)
0716:                                extend.append(',');
0717:                            extend.append(c.getName());
0718:                        }
0719:                    }
0720:                    classElement.setAttribute("extends", extend.toString());
0721:                }
0722:
0723:                xmlValue(getElement(parent, "default",
0724:                        "description,class,property,default"), defaultValue);
0725:
0726:                addRestriction(parent, "unique",
0727:                        "description,class,property,default,unique",
0728:                        uniqueRestriction);
0729:                addRestriction(parent, "required",
0730:                        "description,class,property,default,unique,required",
0731:                        requiredRestriction);
0732:                getElement(parent, "enumeration",
0733:                        "description,class,property,default,unique,required,enumeration");
0734:                /// set this here...
0735:
0736:                /**
0737:                   End up in the wrong place, and not needed for javascript, so commented out for the moment.
0738:
0739:                if (getCommitProcessor() != EmptyCommitProcessor.getInstance()) {
0740:                    org.w3c.dom.NodeList nl  = parent.getElementsByTagName("commitprocessor");
0741:                    Element element;
0742:                    if (nl.getLength() == 0) {
0743:                        element = parent.getOwnerDocument().createElementNS(XMLNS, "commitprocessor");
0744:                        Element clazz = parent.getOwnerDocument().createElementNS(XMLNS, "class");
0745:                        clazz.setAttribute("name", getCommitProcessor().getClass().getName());
0746:                        DocumentReader.appendChild(parent, element, "description,class,property");
0747:                        element.appendChild(clazz);
0748:                    } else {
0749:                        element = (Element) nl.item(0);
0750:                    }
0751:
0752:                    //element.setAttribute("value", Casting.toString(defaultValue));
0753:                }
0754:                 */
0755:
0756:            }
0757:
0758:            public int compareTo(DataType<C> a) {
0759:                int compared = getName().compareTo(a.getName());
0760:                if (compared == 0)
0761:                    compared = getTypeAsClass().getName().compareTo(
0762:                            a.getTypeAsClass().getName());
0763:                return compared;
0764:            }
0765:
0766:            /**
0767:             * Whether data type equals to other data type. Only key and type are consided. DefaultValue and
0768:             * required properties are only 'utilities'.
0769:             * @return true if o is a DataType of which key and type equal to this' key and type.
0770:             */
0771:            public boolean equals(Object o) {
0772:                if (o instanceof  DataType) {
0773:                    DataType<?> a = (DataType<?>) o;
0774:                    return getName().equals(a.getName())
0775:                            && getTypeAsClass().equals(a.getTypeAsClass());
0776:                }
0777:                return false;
0778:            }
0779:
0780:            public int hashCode() {
0781:                return getName().hashCode() * 13 + getTypeAsClass().hashCode();
0782:            }
0783:
0784:            /**
0785:             * {@inheritDoc}
0786:             */
0787:            public boolean isRequired() {
0788:                return requiredRestriction.isRequired();
0789:            }
0790:
0791:            /**
0792:             * {@inheritDoc}
0793:             */
0794:            public DataType.Restriction<Boolean> getRequiredRestriction() {
0795:                return requiredRestriction;
0796:            }
0797:
0798:            /**
0799:             * {@inheritDoc}
0800:             */
0801:            public void setRequired(boolean required) {
0802:                getRequiredRestriction().setValue(Boolean.valueOf(required));
0803:            }
0804:
0805:            /**
0806:             * {@inheritDoc}
0807:             */
0808:            public boolean isUnique() {
0809:                return uniqueRestriction.isUnique();
0810:            }
0811:
0812:            /**
0813:             * {@inheritDoc}
0814:             */
0815:            public DataType.Restriction<Boolean> getUniqueRestriction() {
0816:                return uniqueRestriction;
0817:            }
0818:
0819:            /**
0820:             * {@inheritDoc}
0821:             */
0822:            public void setUnique(boolean unique) {
0823:                getUniqueRestriction().setValue(Boolean.valueOf(unique));
0824:            }
0825:
0826:            /**
0827:             * {@inheritDoc}
0828:             */
0829:            public String getEnumerationValue(Locale locale, Cloud cloud,
0830:                    Node node, Field field, Object key) {
0831:                String value = null;
0832:                if (key != null) {
0833:                    // cast to the appropriate datatype value.
0834:                    // Note that for now it is assumed that the keys are of the same type.
0835:                    // I'm not 100% sure that this is always the case.
0836:                    C keyValue = cast(key, node, field);
0837:                    if (keyValue != null) {
0838:                        for (Iterator<Map.Entry<C, String>> i = new RestrictedEnumerationIterator(
0839:                                locale, cloud, node, field); value == null
0840:                                && i.hasNext();) {
0841:                            Map.Entry<C, String> entry = i.next();
0842:                            if (keyValue.equals(entry.getKey())) {
0843:                                value = entry.getValue();
0844:                            }
0845:                        }
0846:                    }
0847:                }
0848:                return value;
0849:            }
0850:
0851:            /**
0852:             * {@inheritDoc}
0853:             */
0854:            public Iterator<Map.Entry<C, String>> getEnumerationValues(
0855:                    Locale locale, Cloud cloud, Node node, Field field) {
0856:                Iterator<Map.Entry<C, String>> i = new RestrictedEnumerationIterator(
0857:                        locale, cloud, node, field);
0858:                return i.hasNext() ? i : null;
0859:            }
0860:
0861:            /**
0862:             * {@inheritDoc}
0863:             */
0864:            public LocalizedEntryListFactory<C> getEnumerationFactory() {
0865:                return enumerationRestriction.getEnumerationFactory();
0866:            }
0867:
0868:            /**
0869:             * {@inheritDoc}
0870:             */
0871:            public DataType.Restriction<LocalizedEntryListFactory<C>> getEnumerationRestriction() {
0872:                return enumerationRestriction;
0873:            }
0874:
0875:            public CommitProcessor getCommitProcessor() {
0876:                return commitProcessor == null ? EmptyCommitProcessor
0877:                        .getInstance() : commitProcessor;
0878:            }
0879:
0880:            public void setCommitProcessor(CommitProcessor cp) {
0881:                commitProcessor = cp;
0882:            }
0883:
0884:            /**
0885:             * {@inheritDoc}
0886:             */
0887:            public Processor getProcessor(int action) {
0888:                Processor processor;
0889:                if (action == PROCESS_GET) {
0890:                    processor = getProcessors == null ? null : getProcessors[0];
0891:                } else {
0892:                    processor = setProcessors == null ? null : setProcessors[0];
0893:                }
0894:                return processor == null ? CopyProcessor.getInstance()
0895:                        : processor;
0896:            }
0897:
0898:            /**
0899:             * {@inheritDoc}
0900:             */
0901:            public Processor getProcessor(int action, int processingType) {
0902:                if (processingType == Field.TYPE_UNKNOWN) {
0903:                    return getProcessor(action);
0904:                } else {
0905:                    Processor processor;
0906:                    if (action == PROCESS_GET) {
0907:                        processor = getProcessors == null ? null
0908:                                : getProcessors[processingType];
0909:                    } else {
0910:                        processor = setProcessors == null ? null
0911:                                : setProcessors[processingType];
0912:                    }
0913:                    return processor == null ? getProcessor(action) : processor;
0914:                }
0915:            }
0916:
0917:            /**
0918:             * {@inheritDoc}
0919:             */
0920:            public void setProcessor(int action, Processor processor) {
0921:                setProcessor(action, processor, Field.TYPE_UNKNOWN);
0922:            }
0923:
0924:            private Processor[] newProcessorsArray() {
0925:                return new Processor[] { null /* object   */,
0926:                        null /* string  */, null /* integer */,
0927:                        null /* not used */, null /* byte */,
0928:                        null /* float    */, null /* double  */,
0929:                        null /* long    */, null /* xml      */,
0930:                        null /* node */, null /* datetime */,
0931:                        null /* boolean */, null /* list    */
0932:                };
0933:            }
0934:
0935:            /**
0936:             * {@inheritDoc}
0937:             */
0938:            public void setProcessor(int action, Processor processor,
0939:                    int processingType) {
0940:                if (processingType == Field.TYPE_UNKNOWN) {
0941:                    processingType = 0;
0942:                }
0943:                if (action == PROCESS_GET) {
0944:                    if (getProcessors == null)
0945:                        getProcessors = newProcessorsArray();
0946:                    getProcessors[processingType] = processor;
0947:                } else {
0948:                    if (setProcessors == null)
0949:                        setProcessors = newProcessorsArray();
0950:                    setProcessors[processingType] = processor;
0951:                }
0952:            }
0953:
0954:            // ================================================================================
0955:            // Follow implementations of the basic restrictions.
0956:
0957:            // ABSTRACT
0958:
0959:            /**
0960:             * Abstract inner class Restriction. Based on static StaticAbstractRestriction
0961:             */
0962:            protected abstract class AbstractRestriction<D extends Serializable>
0963:                    extends StaticAbstractRestriction<D> {
0964:                protected AbstractRestriction(AbstractRestriction source) {
0965:                    super (BasicDataType.this , source);
0966:                }
0967:
0968:                protected AbstractRestriction(String name, D value) {
0969:                    super (BasicDataType.this , name, value);
0970:                }
0971:            }
0972:
0973:            /**
0974:             * A Restriction is represented by these kind of objects.
0975:             * When you override this class, take care of cloning of outer class!
0976:             * This class itself is not cloneable. Cloning is hard when you have inner classes.
0977:             *
0978:             * All restrictions extend from this.
0979:             *
0980:             * See <a href="http://www.adtmag.com/java/articleold.asp?id=364">article about inner classes,
0981:             * cloning in java</a>
0982:             */
0983:            protected static abstract class StaticAbstractRestriction<D extends Serializable>
0984:                    implements  DataType.Restriction<D> {
0985:                protected final String name;
0986:                protected final BasicDataType parent;
0987:                protected LocalizedString errorDescription;
0988:                protected D value;
0989:                protected boolean fixed = false;
0990:                protected int enforceStrength = DataType.ENFORCE_ALWAYS;
0991:
0992:                /**
0993:                 * If a restriction has an 'absolute' parent restriction, then also that restriction must be
0994:                 * valid (because it was 'absolute'). A restriction gets an absolute parent if its
0995:                 * surrounding DataType is clone of DataType in which the same restriction is marked with
0996:                 * {@link DataType#ENFORCE_ABSOLUTE}.
0997:                 */
0998:                protected StaticAbstractRestriction absoluteParent = null;
0999:
1000:                /**
1001:                 * Instantaties new restriction for a clone of the parent DataType. If the source
1002:                 * restriction is 'absolute' it will remain to be enforced even if the clone gets a new
1003:                 * value.
1004:                 */
1005:                protected StaticAbstractRestriction(BasicDataType parent,
1006:                        StaticAbstractRestriction source) {
1007:                    this .name = source.getName();
1008:                    this .parent = parent;
1009:                    if (source.enforceStrength == DataType.ENFORCE_ABSOLUTE) {
1010:                        absoluteParent = source;
1011:                    } else {
1012:                        absoluteParent = source.absoluteParent;
1013:                    }
1014:                    inherit(source);
1015:                    if (source.enforceStrength == DataType.ENFORCE_ABSOLUTE) {
1016:                        enforceStrength = DataType.ENFORCE_ALWAYS;
1017:                    }
1018:                }
1019:
1020:                protected StaticAbstractRestriction(BasicDataType parent,
1021:                        String name, D value) {
1022:                    this .name = name;
1023:                    this .parent = parent;
1024:                    this .value = value;
1025:                }
1026:
1027:                public String getName() {
1028:                    return name;
1029:                }
1030:
1031:                public D getValue() {
1032:                    return value;
1033:                }
1034:
1035:                public void setValue(D v) {
1036:                    parent.edit();
1037:                    if (fixed) {
1038:                        throw new IllegalStateException("Restriction '" + name
1039:                                + "' is fixed, cannot be changed");
1040:                    }
1041:
1042:                    this .value = v;
1043:                }
1044:
1045:                public LocalizedString getErrorDescription() {
1046:                    if (errorDescription == null) {
1047:                        // this is postponsed to first use, because otherwise 'getBaseTypeIdentifier' give correct value only after constructor of parent.
1048:                        String key = parent.getBaseTypeIdentifier() + "."
1049:                                + name + ".error";
1050:                        errorDescription = new LocalizedString(key);
1051:                        errorDescription.setBundle(DATATYPE_BUNDLE);
1052:                    }
1053:                    return errorDescription;
1054:                }
1055:
1056:                public void setErrorDescription(LocalizedString errorDescription) {
1057:                    this .errorDescription = errorDescription;
1058:                }
1059:
1060:                public boolean isFixed() {
1061:                    return fixed;
1062:                }
1063:
1064:                public void setFixed(boolean fixed) {
1065:                    if (this .fixed && !fixed) {
1066:                        throw new IllegalStateException("Restriction '" + name
1067:                                + "' is fixed, cannot be changed");
1068:                    }
1069:                    this .fixed = fixed;
1070:                }
1071:
1072:                /**
1073:                 * Utility method to add a new error message to the errors collection, based on this
1074:                 * Restriction. If this error-collection is unmodifiable (VALID), it is replaced with a new
1075:                 * empty one first.
1076:                 */
1077:                protected final Collection<LocalizedString> addError(
1078:                        Collection<LocalizedString> errors, Object v,
1079:                        Node node, Field field) {
1080:                    if (errors == VALID)
1081:                        errors = new ArrayList<LocalizedString>();
1082:                    ReplacingLocalizedString error = new ReplacingLocalizedString(
1083:                            getErrorDescription());
1084:                    error.replaceAll("\\$\\{NAME\\}", ReplacingLocalizedString
1085:                            .makeLiteral(getName()));
1086:                    error.replaceAll("\\$\\{CONSTRAINT\\}",
1087:                            ReplacingLocalizedString.makeLiteral(toString(node,
1088:                                    field)));
1089:                    error.replaceAll("\\$\\{CONSTRAINTVALUE\\}",
1090:                            ReplacingLocalizedString.makeLiteral(valueString(
1091:                                    node, field)));
1092:                    error.replaceAll("\\$\\{VALUE\\}", ReplacingLocalizedString
1093:                            .makeLiteral(Casting.toString(v)));
1094:                    errors.add(error);
1095:                    return errors;
1096:                }
1097:
1098:                /**
1099:                 * If value of a a restriction depends on node, field, then you can override this
1100:                 */
1101:                protected String valueString(Node node, Field field) {
1102:                    return Casting.toString(value);
1103:                }
1104:
1105:                /**
1106:                 * Whether {@link #validate} must enforce this condition
1107:                 */
1108:                protected final boolean enforce(Object v, Node node, Field field) {
1109:                    switch (enforceStrength) {
1110:                    case DataType.ENFORCE_ABSOLUTE:
1111:                    case DataType.ENFORCE_ALWAYS:
1112:                        return true;
1113:                    case DataType.ENFORCE_ONCHANGE:
1114:                        if (node == null || field == null
1115:                                || node.isChanged(field.getName()))
1116:                            return true;
1117:                    case DataType.ENFORCE_ONCREATE:
1118:                        if (node == null || node.isNew())
1119:                            return true;
1120:                    case DataType.ENFORCE_NEVER:
1121:                        return false;
1122:                    default:
1123:                        return true;
1124:                    }
1125:                }
1126:
1127:                /**
1128:                 * This method is called by {@link BasicDataType#validate(Object, Node, Field)} for each of its conditions.
1129:                 */
1130:                protected Collection<LocalizedString> validate(
1131:                        Collection<LocalizedString> errors, Object v,
1132:                        Node node, Field field) {
1133:                    if (absoluteParent != null
1134:                            && !absoluteParent.valid(v, node, field)) {
1135:                        int sizeBefore = errors.size();
1136:                        Collection<LocalizedString> res = absoluteParent
1137:                                .addError(errors, v, node, field);
1138:                        if (res.size() > sizeBefore) {
1139:                            return res;
1140:                        }
1141:                    }
1142:                    if ((!enforce(v, node, field)) || valid(v, node, field)) {
1143:                        // no new error to add.
1144:                        return errors;
1145:                    } else {
1146:                        return addError(errors, v, node, field);
1147:                    }
1148:                }
1149:
1150:                public final boolean valid(Object v, Node node, Field field) {
1151:                    try {
1152:                        if (absoluteParent != null) {
1153:                            if (!absoluteParent.valid(v, node, field))
1154:                                return false;
1155:                        }
1156:                        return simpleValid(parent
1157:                                .castToValidate(v, node, field), node, field);
1158:                    } catch (Throwable t) {
1159:                        if (log.isServiceEnabled()) {
1160:                            log.service(
1161:                                    "Not valid because cast-to-validate threw exception "
1162:                                            + t.getClass(), t);
1163:                        }
1164:                        return false;
1165:                    }
1166:                }
1167:
1168:                protected abstract boolean simpleValid(Object v, Node node,
1169:                        Field field);
1170:
1171:                protected final void inherit(
1172:                        StaticAbstractRestriction<D> source, boolean cast) {
1173:                    // perhaps this value must be cloned?, but how?? Cloneable has no public methods....
1174:                    D inheritedValue = source.getValue();
1175:                    if (cast)
1176:                        inheritedValue = (D) parent.cast(inheritedValue, null,
1177:                                null);
1178:                    setValue(inheritedValue);
1179:                    enforceStrength = source.getEnforceStrength();
1180:                    errorDescription = (LocalizedString) source
1181:                            .getErrorDescription().clone();
1182:                }
1183:
1184:                protected final void inherit(StaticAbstractRestriction source) {
1185:                    inherit(source, false);
1186:                }
1187:
1188:                public int getEnforceStrength() {
1189:                    return enforceStrength;
1190:                }
1191:
1192:                public void setEnforceStrength(int e) {
1193:                    enforceStrength = e;
1194:                }
1195:
1196:                public final String toString() {
1197:                    return toString(null, null);
1198:                }
1199:
1200:                public final String toString(Node node, Field field) {
1201:                    return name
1202:                            + ": "
1203:                            + (enforceStrength == DataType.ENFORCE_NEVER ? "*"
1204:                                    : "") + valueString(node, field)
1205:                            + (fixed ? "." : "");
1206:                }
1207:
1208:            }
1209:
1210:            // REQUIRED
1211:            protected class RequiredRestriction extends
1212:                    AbstractRestriction<Boolean> {
1213:                private static final long serialVersionUID = 1L;
1214:
1215:                RequiredRestriction(RequiredRestriction source) {
1216:                    super (source);
1217:                }
1218:
1219:                RequiredRestriction(boolean b) {
1220:                    super ("required", Boolean.valueOf(b));
1221:                }
1222:
1223:                final boolean isRequired() {
1224:                    return Boolean.TRUE.equals(value);
1225:                }
1226:
1227:                protected boolean simpleValid(Object v, Node node, Field field) {
1228:                    if (!isRequired())
1229:                        return true;
1230:                    return v != null;
1231:                }
1232:            }
1233:
1234:            // UNIQUE
1235:            protected class UniqueRestriction extends
1236:                    AbstractRestriction<Boolean> {
1237:                private static final long serialVersionUID = 1L;
1238:
1239:                UniqueRestriction(UniqueRestriction source) {
1240:                    super (source);
1241:                }
1242:
1243:                UniqueRestriction(boolean b) {
1244:                    super ("unique", Boolean.valueOf(b));
1245:                }
1246:
1247:                final boolean isUnique() {
1248:                    return Boolean.TRUE.equals(value);
1249:                }
1250:
1251:                protected boolean simpleValid(Object v, Node node, Field field) {
1252:                    if (!isUnique())
1253:                        return true;
1254:                    if (field != null && v != null && value != null) {
1255:
1256:                        if (field.isVirtual()) {
1257:                            log.warn("Cannot check uniqueness on field "
1258:                                    + field + " because it is virtual");
1259:                            return true; // e.g. if the field was defined in XML but not present in DB (after upgrade?)
1260:                        }
1261:
1262:                        if (node != null && !node.isNew()) {
1263:                            if (field.getName().equals("number")) {
1264:                                // on 'number' there is a unique constraint, if it is checked for a non-new node
1265:                                // we can simply avoid all quering because it will result in a query number == <number> and number <> <number>
1266:                                if (Casting.toInt(v) == node.getNumber()) {
1267:                                    return true;
1268:                                } else {
1269:                                    // changing
1270:                                    log.warn("Odd, changing number of node "
1271:                                            + node + " ?!", new Exception());
1272:                                }
1273:                            }
1274:                        }
1275:
1276:                        NodeManager nodeManager = field.getNodeManager();
1277:                        Cloud cloud = nodeManager.getCloud();
1278:                        if (cloud.getUser().getRank().getInt() < Rank.ADMIN_INT) {
1279:                            // This will test for uniqueness using bridge, so you'll miss objects you can't
1280:                            // see (and database doesn't know that!)
1281:                            // So try using an administrator for that! That would probably work ok.
1282:                            Cloud adminCloud = cloud.getCloudContext()
1283:                                    .getCloud("mmbase", "class", null);
1284:                            if (adminCloud.getUser().getRank().getInt() > cloud
1285:                                    .getUser().getRank().getInt()) {
1286:                                cloud = adminCloud;
1287:                                nodeManager = adminCloud
1288:                                        .getNodeManager(nodeManager.getName());
1289:                            }
1290:                        }
1291:                        // create a query and query for the value
1292:                        NodeQuery query = nodeManager.createQuery();
1293:                        Constraint constraint = Queries.createConstraint(query,
1294:                                field.getName(), FieldCompareConstraint.EQUAL,
1295:                                v);
1296:                        Queries.addConstraint(query, constraint);
1297:                        if (node != null && !node.isNew()) {
1298:                            constraint = Queries.createConstraint(query,
1299:                                    "number", FieldCompareConstraint.NOT_EQUAL,
1300:                                    node.getNumber());
1301:                            Queries.addConstraint(query, constraint);
1302:                        }
1303:                        if (log.isDebugEnabled()) {
1304:                            log.debug(query);
1305:                        }
1306:                        return Queries.count(query) == 0;
1307:                    } else {
1308:                        if (field == null)
1309:                            log.warn("Cannot check uniqueness  without field");
1310:                        return true;
1311:                    }
1312:                }
1313:            }
1314:
1315:            // TYPE
1316:
1317:            protected class TypeRestriction extends
1318:                    AbstractRestriction<Class<?>> {
1319:                private static final long serialVersionUID = 1L;
1320:
1321:                TypeRestriction(TypeRestriction source) {
1322:                    super (source);
1323:                }
1324:
1325:                TypeRestriction() {
1326:                    super ("type", BasicDataType.this .getClass());
1327:                }
1328:
1329:                protected boolean simpleValid(Object v, Node node, Field field) {
1330:                    try {
1331:                        BasicDataType.this .cast(v, node, field);
1332:                        return true;
1333:                    } catch (Throwable e) {
1334:                        log.error(e);
1335:                        return false;
1336:                    }
1337:                }
1338:            }
1339:
1340:            // ENUMERATION
1341:            protected class EnumerationRestriction extends
1342:                    AbstractRestriction<LocalizedEntryListFactory<C>> {
1343:                private static final long serialVersionUID = 1L;
1344:
1345:                EnumerationRestriction(EnumerationRestriction source) {
1346:                    super (source);
1347:                    value = value != null ? (LocalizedEntryListFactory<C>) value
1348:                            .clone()
1349:                            : null;
1350:                }
1351:
1352:                EnumerationRestriction(LocalizedEntryListFactory<C> entries) {
1353:                    super ("enumeration", entries);
1354:                }
1355:
1356:                final LocalizedEntryListFactory<C> getEnumerationFactory() {
1357:                    if (value == null) {
1358:                        value = new LocalizedEntryListFactory<C>();
1359:                    }
1360:                    return value;
1361:                }
1362:
1363:                public Collection<Map.Entry<C, String>> getEnumeration(
1364:                        Locale locale, Cloud cloud, Node node, Field field) {
1365:                    if (value == null)
1366:                        return Collections.emptyList();
1367:                    if (cloud == null) {
1368:                        if (node != null) {
1369:                            cloud = node.getCloud();
1370:                        } else if (field != null) {
1371:                            cloud = field.getNodeManager().getCloud();
1372:                        }
1373:                    }
1374:                    return value.get(locale, cloud);
1375:                }
1376:
1377:                /**
1378:                 * @see BasicDataType#preCast
1379:                 */
1380:                protected <D> D preCast(D v, Cloud cloud) {
1381:                    if (getValue() == null)
1382:                        return v;
1383:                    try {
1384:                        return (D) value.castKey(v, cloud);
1385:                        //return v != null ? Casting.toType(v.getClass(), cloud, res) : res;
1386:                    } catch (NoClassDefFoundError ncdfe) {
1387:                        log.error("Could not find class " + ncdfe.getMessage()
1388:                                + " while casting " + v.getClass() + " " + v,
1389:                                ncdfe);
1390:                        return v;
1391:                    }
1392:
1393:                }
1394:
1395:                protected boolean simpleValid(Object v, Node node, Field field) {
1396:                    if (value == null || value.isEmpty()) {
1397:                        return true;
1398:                    }
1399:                    Cloud cloud = BasicDataType.this .getCloud(node, field);
1400:                    Collection<Map.Entry<C, String>> validValues = getEnumeration(
1401:                            null, cloud, node, field);
1402:                    if (validValues.size() == 0) {
1403:                        return true;
1404:                    }
1405:                    Object candidate;
1406:                    try {
1407:                        candidate = BasicDataType.this .cast(v, cloud, node,
1408:                                field);
1409:                    } catch (CastException ce) {
1410:                        log.info(ce);
1411:                        return false;
1412:                    }
1413:                    for (Map.Entry<C, String> e : validValues) {
1414:                        Object valid = e.getKey();
1415:                        if (valid.equals(candidate)) {
1416:                            return true;
1417:                        }
1418:                    }
1419:                    return false;
1420:                }
1421:
1422:                protected String valueString(Node node, Field field) {
1423:                    Collection<Map.Entry<C, String>> col = getEnumeration(null,
1424:                            null, node, field);
1425:                    if (col.size() == 0)
1426:                        return "";
1427:                    StringBuffer buf = new StringBuffer();
1428:                    Iterator<Map.Entry<C, String>> it = col.iterator();
1429:                    int i = 0;
1430:                    while (it.hasNext() && ++i < 10) {
1431:                        Map.Entry<C, String> ent = it.next();
1432:                        buf.append(Casting.toString(ent));
1433:                        if (it.hasNext())
1434:                            buf.append(", ");
1435:                    }
1436:                    if (i < col.size())
1437:                        buf.append(".(" + (col.size() - i) + " more ..");
1438:                    return buf.toString();
1439:                }
1440:
1441:            }
1442:
1443:            /**
1444:             * Iterates over the collection provided by the EnumerationRestriction, but skips the values
1445:             * which are invalid because of the other restrictions on this DataType.
1446:             */
1447:            //Also, it 'preCasts' the * keys to the right type.
1448:            protected class RestrictedEnumerationIterator implements 
1449:                    Iterator<Map.Entry<C, String>> {
1450:                private final Iterator<Map.Entry<C, String>> baseIterator;
1451:                private final Node node;
1452:                private final Field field;
1453:                private Map.Entry<C, String> next = null;
1454:
1455:                RestrictedEnumerationIterator(Locale locale, Cloud cloud,
1456:                        Node node, Field field) {
1457:                    Collection<Map.Entry<C, String>> col = enumerationRestriction
1458:                            .getEnumeration(locale, cloud, node, field);
1459:                    if (log.isDebugEnabled()) {
1460:                        log.debug("Restricted iterator on " + col);
1461:                    }
1462:                    baseIterator = col.iterator();
1463:                    this .node = node;
1464:                    this .field = field;
1465:                    determineNext();
1466:                }
1467:
1468:                protected void determineNext() {
1469:                    next = null;
1470:                    while (baseIterator.hasNext()) {
1471:                        final Map.Entry<C, String> entry = baseIterator.next();
1472:                        C value = entry.getKey();
1473:                        Collection<LocalizedString> validationResult = BasicDataType.this 
1474:                                .validate(value, node, field, false);
1475:                        if (validationResult == VALID) {
1476:                            next = entry;
1477:                            /*
1478:                            new Map.Entry() {
1479:                                    public Object getKey() {
1480:                                        return BasicDataType.this.preCast(entry.getKey(), node, field);
1481:                                    }
1482:                                    public Object getValue() {
1483:                                        return entry.getValue();
1484:                                    }
1485:                                    public Object setValue(Object v) {
1486:                                        return entry.setValue(v);
1487:                                    }
1488:                                };
1489:                             */
1490:                            break;
1491:                        } else if (log.isDebugEnabled()) {
1492:                            String errors = "";
1493:                            for (LocalizedString localizedString : validationResult) {
1494:                                errors += localizedString.get(null);
1495:                            }
1496:                            log.debug("Value " + value.getClass() + " " + value
1497:                                    + " does not validate : " + errors);
1498:                        }
1499:                    }
1500:                }
1501:
1502:                public boolean hasNext() {
1503:                    return next != null;
1504:                }
1505:
1506:                public Map.Entry<C, String> next() {
1507:                    if (next == null) {
1508:                        throw new NoSuchElementException();
1509:                    }
1510:                    Map.Entry<C, String> n = next;
1511:                    determineNext();
1512:                    return n;
1513:                }
1514:
1515:                public void remove() {
1516:                    throw new UnsupportedOperationException(
1517:                            "Cannot remove entries from " + getClass());
1518:                }
1519:
1520:                public String toString() {
1521:                    return "restricted iterator(" + enumerationRestriction
1522:                            + ")";
1523:                }
1524:            }
1525:
1526:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.