1 10 11 package org.mmbase.datatypes; 12 13 import java.io.*; 14 import java.util.*; 15 16 import org.mmbase.bridge.*; 17 import org.mmbase.bridge.util.Queries; 18 import org.mmbase.core.AbstractDescriptor; 19 import org.mmbase.core.util.Fields; 20 import org.mmbase.datatypes.processors.*; 21 import org.mmbase.security.Rank; 22 import org.mmbase.storage.search.Constraint; 23 import org.mmbase.storage.search.FieldCompareConstraint; 24 import org.mmbase.util.*; 25 import org.mmbase.util.logging.Logger; 26 import org.mmbase.util.logging.Logging; 27 import org.mmbase.util.xml.DocumentReader; 28 import org.w3c.dom.Element ; 29 30 31 43 44 public class BasicDataType extends AbstractDescriptor implements DataType, Cloneable , Comparable , Descriptor { 45 49 public static final String DATATYPE_BUNDLE = "org.mmbase.datatypes.resources.datatypes"; 50 private static final Logger log = Logging.getLoggerInstance(BasicDataType.class); 51 52 protected RequiredRestriction requiredRestriction = new RequiredRestriction(false); 53 protected UniqueRestriction uniqueRestriction = new UniqueRestriction(false); 54 protected TypeRestriction typeRestriction = new TypeRestriction(); 55 protected EnumerationRestriction enumerationRestriction = new EnumerationRestriction((LocalizedEntryListFactory) null); 56 57 60 protected DataType origin = null; 61 62 private Object owner; 63 private Class classType; 64 private Object defaultValue; 65 66 private CommitProcessor commitProcessor = EmptyCommitProcessor.getInstance(); 67 private Processor[] getProcessors; 68 private Processor[] setProcessors; 69 70 private Element xml = null; 71 72 76 public BasicDataType(String name) { 77 this(name, Object .class); 78 } 79 80 85 protected BasicDataType(String name, Class classType) { 86 super(name); 87 this.classType = classType; 88 owner = null; 89 } 90 91 private static final long serialVersionUID = 1L; 93 private void writeObject(ObjectOutputStream out) throws IOException { 95 out.writeUTF(key); 96 out.writeObject(description); 97 out.writeObject(guiName); 98 out.writeObject(requiredRestriction); 99 out.writeObject(uniqueRestriction); 100 out.writeObject(enumerationRestriction.getEnumerationFactory()); 101 if (owner instanceof Serializable) { 102 out.writeObject(owner); 103 } else { 104 out.writeObject(owner == null ? null : "OWNER"); 105 } 106 out.writeObject(classType); 107 if (defaultValue instanceof Serializable || defaultValue == null) { 108 out.writeObject(defaultValue); 109 } else { 110 log.warn("Default value " + defaultValue.getClass() + " '" + defaultValue + "' is not serializable, taking it null, which may not be correct."); 111 out.writeObject(null); 112 } 113 out.writeObject(commitProcessor); 114 out.writeObject(getProcessors); 115 out.writeObject(setProcessors); 116 } 117 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 119 key = in.readUTF(); 120 description = (LocalizedString) in.readObject(); 121 guiName = (LocalizedString) in.readObject(); 122 requiredRestriction = (RequiredRestriction) in.readObject(); 123 uniqueRestriction = (UniqueRestriction) in.readObject(); 124 enumerationRestriction = new EnumerationRestriction((LocalizedEntryListFactory) in.readObject()); 125 typeRestriction = new TypeRestriction(); owner = in.readObject(); 127 try { 128 classType = (Class ) in.readObject(); 129 } catch (Throwable t) { 130 classType = Object .class; 132 } 133 defaultValue = in.readObject(); 134 commitProcessor = (CommitProcessor) in.readObject(); 135 getProcessors = (Processor[]) in.readObject(); 136 setProcessors = (Processor[]) in.readObject(); 137 } 138 139 public String getBaseTypeIdentifier() { 140 return Fields.getTypeDescription(getBaseType()).toLowerCase(); 141 } 142 143 public int getBaseType() { 144 return Fields.classToType(classType); 145 } 146 147 151 public final void inherit(BasicDataType origin) { 152 edit(); 153 inheritProperties(origin); 154 inheritRestrictions(origin); 155 } 156 157 160 protected void inheritProperties(BasicDataType origin) { 161 this.origin = origin; 162 163 defaultValue = origin.getDefaultValue(); 164 165 commitProcessor = (CommitProcessor) ( origin.commitProcessor instanceof PublicCloneable ? ((PublicCloneable) origin.commitProcessor).clone() : origin.commitProcessor); 166 if (origin.getProcessors == null) { 167 getProcessors = null; 168 } else { 169 getProcessors = (Processor[]) origin.getProcessors.clone(); 170 } 171 if (origin.setProcessors == null) { 172 setProcessors = null; 173 } else { 174 setProcessors = (Processor[]) origin.setProcessors.clone(); 175 } 176 } 177 178 181 protected void cloneRestrictions(BasicDataType origin) { 182 enumerationRestriction = new EnumerationRestriction(origin.enumerationRestriction); 183 requiredRestriction = new RequiredRestriction(origin.requiredRestriction); 184 uniqueRestriction = new UniqueRestriction(origin.uniqueRestriction); 185 } 186 187 190 protected void inheritRestrictions(BasicDataType origin) { 191 if (! origin.getEnumerationFactory().isEmpty()) { 192 enumerationRestriction.inherit(origin.enumerationRestriction); 193 if (enumerationRestriction.value != null) { 194 LocalizedEntryListFactory fact = enumerationRestriction.getEnumerationFactory(); 195 if (! origin.getTypeAsClass().equals(getTypeAsClass())) { 196 Element elm = fact.toXml(); 198 if (elm == null) { 199 log.warn("Did not get XML from Factory " + fact); 200 } else { 201 fact = new LocalizedEntryListFactory(); 204 fact.fillFromXml(elm, getTypeAsClass()); 205 enumerationRestriction.setValue(fact); 206 } 207 } 208 } 209 } 210 211 requiredRestriction.inherit(origin.requiredRestriction); 212 uniqueRestriction.inherit(origin.uniqueRestriction); 213 } 214 215 218 public DataType getOrigin() { 219 return origin; 220 } 221 222 225 public Class getTypeAsClass() { 226 return classType; 227 } 228 229 235 protected boolean isCorrectType(Object value) { 236 return Casting.isType(classType, value); 237 } 238 239 242 public void checkType(Object value) { 243 if (!isCorrectType(value)) { 244 throw new IllegalArgumentException ("DataType of '" + value + "' for '" + getName() + "' must be of type " + classType + " (but is " + (value == null ? value : value.getClass()) + ")"); 246 } 247 } 248 249 250 255 public final Object preCast(Object value, Node node, Field field) { 256 return preCast(value, getCloud(node, field), node, field); 257 } 258 259 262 public final Object preCast(Object value, Cloud cloud) { 263 return preCast(value, cloud, null, null); 264 } 265 266 271 protected Object preCast(Object value, Cloud cloud, Node node, Field field) { 272 if (value == null) return null; 273 Object preCast = enumerationRestriction.preCast(value, cloud); 274 return preCast; 275 } 276 277 278 286 public final Object cast(Object value, final Node node, final Field field) { 287 if (origin != null && (! origin.getClass().isAssignableFrom(getClass()))) { 288 value = origin.cast(value, node, field); 291 } 292 Cloud cloud = getCloud(node, field); 293 try { 294 return cast(value, cloud, node, field); 295 } catch (CastException ce) { 296 log.error(ce.getMessage()); 297 return Casting.toType(classType, cloud, preCast(value, cloud, node, field)); 298 } 299 } 300 301 304 protected Object cast(Object value, Cloud cloud, Node node, Field field) throws CastException { 305 Object preCast = preCast(value, cloud, node, field); 306 if (preCast == null) return null; 307 Object cast = Casting.toType(classType, cloud, preCast); 308 return cast; 309 } 310 311 protected final Cloud getCloud(Node node, Field field) { 312 if (node != null) return node.getCloud(); 313 if (field != null) return field.getNodeManager().getCloud(); 314 return null; 315 } 316 317 322 protected Object castToValidate(Object value, Node node, Field field) throws CastException { 323 return cast(value, getCloud(node, field), node, field); 324 } 325 326 329 public Object getDefaultValue() { 330 if (defaultValue == null) return null; 331 return cast(defaultValue, null, null); 332 } 333 334 337 public void setDefaultValue(Object def) { 338 edit(); 339 defaultValue = def; 340 } 341 342 protected Element getElement(Element parent, String name, String path) { 343 return getElement(parent, name, name, path); 344 } 345 protected Element getElement(Element parent, String pattern, String name, String path) { 346 java.util.regex.Pattern p = java.util.regex.Pattern.compile("\\A" + pattern + "\\z"); 347 org.w3c.dom.NodeList nl = parent.getChildNodes(); 348 Element el = null; 349 for (int i = 0; i < nl.getLength(); i++) { 350 org.w3c.dom.Node child = nl.item(i); 351 if (child instanceof Element ) { 352 if (p.matcher(child.getNodeName()).matches()) { 353 el = (Element ) child; 354 break; 355 } 356 } 357 } 358 if (el == null) { 359 el = parent.getOwnerDocument().createElementNS(XMLNS, name); 360 DocumentReader.appendChild(parent, el, path); 361 } 362 return el; 363 } 364 365 protected String xmlValue(Object value) { 366 return Casting.toString(value); 367 } 368 369 public void toXml(Element parent) { 370 parent.setAttribute("id", getName()); 371 description.toXml("description", XMLNS, parent, "description"); 372 getElement(parent, "class", "description,class").setAttribute("name", getClass().getName()); 373 getElement(parent, "default", "description,class,property,default").setAttribute("value", xmlValue(defaultValue)); 374 getElement(parent, "unique", "description,class,property,default,unique").setAttribute("value", "" + uniqueRestriction.isUnique()); 375 getElement(parent, "required", "description,class,property,default,unique,required").setAttribute("value", "" + requiredRestriction.isRequired()); 376 377 getElement(parent, "enumeration", "description,class,property,default,unique,required,enumeration"); 378 380 if (getCommitProcessor() != EmptyCommitProcessor.getInstance()) { 381 org.w3c.dom.NodeList nl = parent.getElementsByTagName("commitprocessor"); 382 Element element; 383 if (nl.getLength() == 0) { 384 element = parent.getOwnerDocument().createElementNS(XMLNS, "commitprocessor"); 385 Element clazz = parent.getOwnerDocument().createElementNS(XMLNS, "class"); 386 clazz.setAttribute("name", getCommitProcessor().getClass().getName()); 387 DocumentReader.appendChild(parent, element, "description,class,property"); 388 element.appendChild(clazz); 389 } else { 390 element = (Element ) nl.item(0); 391 } 392 393 } 395 396 } 397 398 public boolean isFinished() { 399 return owner != null; 400 } 401 402 405 public void finish() { 406 finish(new Object ()); 407 } 408 409 412 public void finish(Object owner) { 413 this.owner = owner; 414 } 415 416 419 public DataType rewrite(Object owner) { 420 if (this.owner != null) { 421 if (this.owner != owner) { 422 throw new IllegalArgumentException ("Cannot rewrite this datatype - specified owner is not correct"); 423 } 424 this.owner = null; 425 } 426 return this; 427 } 428 429 430 431 434 protected void edit() { 435 if (isFinished()) { 436 throw new IllegalStateException ("This data type '" + getName() + "' is finished and can no longer be changed."); 437 } 438 } 439 440 441 444 public final Collection validate(Object value) { 445 return validate(value, null, null); 446 } 447 448 449 public final Collection validate(final Object value, final Node node, final Field field) { 450 return validate(value, node, field, true); 451 } 452 455 private final Collection validate(final Object value, final Node node, final Field field, boolean testEnum) { 456 Collection errors = VALID; 457 Object castValue; 458 try { 459 castValue = castToValidate(value, node, field); 460 errors = typeRestriction.validate(errors, castValue, node, field); 461 } catch (CastException ce) { 462 errors = typeRestriction.addError(errors, value, node, field); 463 castValue = value; 464 } 465 466 if (errors.size() > 0) { 467 return errors; 469 } 470 471 errors = requiredRestriction.validate(errors, value, node, field); 472 473 if (value == null) { 474 return errors; } 476 if (testEnum) { 477 errors = enumerationRestriction.validate(errors, value, node, field); 478 } 479 errors = uniqueRestriction.validate(errors, castValue, node, field); 480 errors = validateCastValue(errors, castValue, value, node, field); 481 return errors; 482 } 483 484 protected Collection validateCastValue(Collection errors, Object castValue, Object value, Node node, Field field) { 485 return errors; 486 } 487 488 protected StringBuffer toStringBuffer() { 489 StringBuffer buf = new StringBuffer (); 490 buf.append(getName() + " (" + getTypeAsClass() + (defaultValue != null ? ":" + defaultValue : "") + ")"); 491 buf.append(commitProcessor == null ? "" : " commit: " + commitProcessor + ""); 492 if (getProcessors != null) { 493 for (int i = 0; i < 13; i++) { 494 buf.append(getProcessors[i] == null ? "" : ("; get [" + Fields.typeToClass(i) + "]:" + getProcessors[i] + " ")); 495 } 496 } 497 if (setProcessors != null) { 498 for (int i =0; i < 13; i++) { 499 buf.append(setProcessors[i] == null ? "" : ("; set [" + Fields.typeToClass(i) + "]:" + setProcessors[i] + " ")); 500 } 501 } 502 if (isRequired()) { 503 buf.append(" required"); 504 } 505 if (isUnique()) { 506 buf.append(" unique"); 507 } 508 if (enumerationRestriction.getValue() != null) { 509 buf.append(" " + enumerationRestriction); 510 } 511 return buf; 512 513 } 514 public final String toString() { 515 StringBuffer buf = toStringBuffer(); 516 if (isFinished()) { 517 buf.append("."); 518 } 519 return buf.toString(); 520 } 521 522 523 528 public final Object clone() { 529 return clone(null); 530 } 531 532 538 public Object clone(String name) { 539 try { 540 BasicDataType clone = (BasicDataType) super.clone(name); 541 clone.owner = null; 543 clone.inheritProperties(this); 545 clone.cloneRestrictions(this); 546 if (log.isTraceEnabled()) { 547 log.trace("Cloned " + this + " -> " + clone); 548 } 549 return clone; 550 } catch (CloneNotSupportedException cnse) { 551 log.error("Cannot clone this DataType: " + name); 553 throw new RuntimeException ("Cannot clone this DataType: " + name, cnse); 554 } 555 } 556 557 public Element toXml() { 558 if (xml == null) { 559 xml = DocumentReader.getDocumentBuilder().newDocument().createElementNS(XMLNS, "datatype"); 560 xml.getOwnerDocument().appendChild(xml); 561 } 562 return xml; 563 } 564 565 public void setXml(Element element) { 566 xml = DocumentReader.toDocument(element).getDocumentElement(); 567 if (origin != null) { 568 xml.setAttribute("base", origin.getName()); 569 } 570 org.w3c.dom.NodeList childNodes = xml.getChildNodes(); 572 for (int i = 0; i < childNodes.getLength(); i++) { 573 if (childNodes.item(i) instanceof Element ) { 574 Element childElement = (Element ) childNodes.item(i); 575 if (childElement.getLocalName().equals("specialization") 576 ||childElement.getLocalName().equals("datatype") 577 ) { 578 xml.removeChild(childElement); 579 } 580 } 581 } 582 } 583 584 public int compareTo(Object o) { 585 if (o instanceof DataType) { 586 DataType a = (DataType) o; 587 int compared = getName().compareTo(a.getName()); 588 if (compared == 0) compared = getTypeAsClass().getName().compareTo(a.getTypeAsClass().getName()); 589 return compared; 590 } else { 591 throw new ClassCastException ("Object is not of type DataType"); 592 } 593 } 594 595 600 public boolean equals(Object o) { 601 if (o instanceof DataType) { 602 DataType a = (DataType) o; 603 return getName().equals(a.getName()) && getTypeAsClass().equals(a.getTypeAsClass()); 604 } 605 return false; 606 } 607 608 public int hashCode() { 609 return getName().hashCode() * 13 + getTypeAsClass().hashCode(); 610 } 611 612 615 public boolean isRequired() { 616 return requiredRestriction.isRequired(); 617 } 618 619 622 public DataType.Restriction getRequiredRestriction() { 623 return requiredRestriction; 624 } 625 626 629 public void setRequired(boolean required) { 630 getRequiredRestriction().setValue(Boolean.valueOf(required)); 631 } 632 633 636 public boolean isUnique() { 637 return uniqueRestriction.isUnique(); 638 } 639 640 643 public DataType.Restriction getUniqueRestriction() { 644 return uniqueRestriction; 645 } 646 647 650 public void setUnique(boolean unique) { 651 getUniqueRestriction().setValue(Boolean.valueOf(unique)); 652 } 653 654 657 public Object getEnumerationValue(Locale locale, Cloud cloud, Node node, Field field, Object key) { 658 Object value = null; 659 if (key != null) { 660 Object keyValue = cast(key, node, field); 664 if (keyValue != null) { 665 for (Iterator i = new RestrictedEnumerationIterator(locale, cloud, node, field); value == null && i.hasNext(); ) { 666 Map.Entry entry = (Map.Entry) i.next(); 667 if (keyValue.equals(entry.getKey()) ) { 668 value = entry.getValue(); 669 } 670 } 671 } 672 } 673 return value; 674 } 675 676 679 public Iterator getEnumerationValues(Locale locale, Cloud cloud, Node node, Field field) { 680 Iterator i = new RestrictedEnumerationIterator(locale, cloud, node, field); 681 return i.hasNext() ? i : null; 682 } 683 684 687 public LocalizedEntryListFactory getEnumerationFactory() { 688 return enumerationRestriction.getEnumerationFactory(); 689 } 690 691 694 public DataType.Restriction getEnumerationRestriction() { 695 return enumerationRestriction; 696 } 697 698 699 700 public CommitProcessor getCommitProcessor() { 701 return commitProcessor == null ? EmptyCommitProcessor.getInstance() : commitProcessor; 702 } 703 public void setCommitProcessor(CommitProcessor cp) { 704 commitProcessor = cp; 705 } 706 707 710 public Processor getProcessor(int action) { 711 Processor processor; 712 if (action == PROCESS_GET) { 713 processor = getProcessors == null ? null : getProcessors[0]; 714 } else { 715 processor = setProcessors == null ? null : setProcessors[0]; 716 } 717 return processor == null ? CopyProcessor.getInstance() : processor; 718 } 719 720 723 public Processor getProcessor(int action, int processingType) { 724 if (processingType == Field.TYPE_UNKNOWN) { 725 return getProcessor(action); 726 } else { 727 Processor processor; 728 if (action == PROCESS_GET) { 729 processor = getProcessors == null ? null : getProcessors[processingType]; 730 } else { 731 processor = setProcessors == null ? null : setProcessors[processingType]; 732 } 733 return processor == null ? getProcessor(action) : processor; 734 } 735 } 736 737 740 public void setProcessor(int action, Processor processor) { 741 setProcessor(action, processor, Field.TYPE_UNKNOWN); 742 } 743 744 private Processor[] newProcessorsArray() { 745 return new Processor[] { 746 null , null , null , null , null , 747 null , null , null , null , null , 748 null , null , null 749 }; 750 } 751 752 755 public void setProcessor(int action, Processor processor, int processingType) { 756 if (processingType == Field.TYPE_UNKNOWN) { 757 processingType = 0; 758 } 759 if (action == PROCESS_GET) { 760 if (getProcessors == null) getProcessors = newProcessorsArray(); 761 getProcessors[processingType] = processor; 762 } else { 763 if (setProcessors == null) setProcessors = newProcessorsArray(); 764 setProcessors[processingType] = processor; 765 } 766 } 767 768 769 772 773 775 778 protected abstract class AbstractRestriction extends StaticAbstractRestriction { 779 protected AbstractRestriction(AbstractRestriction source) { 780 super(BasicDataType.this, source); 781 } 782 protected AbstractRestriction(String name, Serializable value) { 783 super(BasicDataType.this, name, value); 784 } 785 } 786 796 protected static abstract class StaticAbstractRestriction implements DataType.Restriction { 797 protected final String name; 798 protected final BasicDataType parent; 799 protected LocalizedString errorDescription; 800 protected Serializable value; 801 protected boolean fixed = false; 802 protected int enforceStrength = DataType.ENFORCE_ALWAYS; 803 804 810 protected StaticAbstractRestriction absoluteParent = null; 811 812 817 protected StaticAbstractRestriction(BasicDataType parent, StaticAbstractRestriction source) { 818 this.name = source.getName(); 819 this.parent = parent; 820 if (source.enforceStrength == DataType.ENFORCE_ABSOLUTE) { 821 absoluteParent = source; 822 } else { 823 absoluteParent = source.absoluteParent; 824 } 825 inherit(source); 826 if (source.enforceStrength == DataType.ENFORCE_ABSOLUTE) { 827 enforceStrength = DataType.ENFORCE_ALWAYS; 828 } 829 } 830 831 protected StaticAbstractRestriction(BasicDataType parent, String name, Serializable value) { 832 this.name = name; 833 this.parent = parent; 834 this.value = value; 835 } 836 837 public String getName() { 838 return name; 839 } 840 841 public Serializable getValue() { 842 return value; 843 } 844 845 public void setValue(Serializable v) { 846 parent.edit(); 847 if (fixed) { 848 throw new IllegalStateException ("Restriction '" + name + "' is fixed, cannot be changed"); 849 } 850 851 this.value = v; 852 } 853 854 public LocalizedString getErrorDescription() { 855 if (errorDescription == null) { 856 String key = parent.getBaseTypeIdentifier() + "." + name + ".error"; 858 errorDescription = new LocalizedString(key); 859 errorDescription.setBundle(DATATYPE_BUNDLE); 860 } 861 return errorDescription; 862 } 863 864 public void setErrorDescription(LocalizedString errorDescription) { 865 this.errorDescription = errorDescription; 866 } 867 868 public boolean isFixed() { 869 return fixed; 870 } 871 872 public void setFixed(boolean fixed) { 873 if (this.fixed && !fixed) { 874 throw new IllegalStateException ("Restriction '" + name + "' is fixed, cannot be changed"); 875 } 876 this.fixed = fixed; 877 } 878 879 884 protected final Collection addError(Collection errors, Object v, Node node, Field field) { 885 if (errors == VALID) errors = new ArrayList(); 886 ReplacingLocalizedString error = new ReplacingLocalizedString(getErrorDescription()); 887 error.replaceAll("\\$\\{NAME\\}", ReplacingLocalizedString.makeLiteral(getName())); 888 error.replaceAll("\\$\\{CONSTRAINT\\}", ReplacingLocalizedString.makeLiteral(toString(node, field))); 889 error.replaceAll("\\$\\{CONSTRAINTVALUE\\}", ReplacingLocalizedString.makeLiteral(valueString(node, field))); 890 error.replaceAll("\\$\\{VALUE\\}", ReplacingLocalizedString.makeLiteral("" + v)); 891 errors.add(error); 892 return errors; 893 } 894 895 898 protected String valueString(Node node, Field field) { 899 return "" + value; 900 } 901 902 905 protected final boolean enforce(Node node, Field field) { 906 switch(enforceStrength) { 907 case DataType.ENFORCE_ABSOLUTE: 908 case DataType.ENFORCE_ALWAYS: return true; 909 case DataType.ENFORCE_ONCHANGE: if (node == null || field == null || node.isChanged(field.getName())) return true; 910 case DataType.ENFORCE_ONCREATE: if (node == null || node.isNew()) return true; 911 case DataType.ENFORCE_NEVER: return false; 912 default: return true; 913 } 914 } 915 916 919 protected Collection validate(Collection errors, Object v, Node node, Field field) { 920 if (absoluteParent != null && ! absoluteParent.valid(v, node, field)) { 921 int sizeBefore = errors.size(); 922 Collection res = absoluteParent.addError(errors, v, node, field); 923 if (res.size() > sizeBefore) { 924 return res; 925 } 926 } 927 if ((! enforce(node, field)) || valid(v, node, field)) { 928 return errors; 930 } else { 931 return addError(errors, v, node, field); 932 } 933 } 934 935 public final boolean valid(Object v, Node node, Field field) { 936 try { 937 if (absoluteParent != null) { 938 if (! absoluteParent.valid(v, node, field)) return false; 939 } 940 return simpleValid(parent.castToValidate(v, node, field), node, field); 941 } catch (Throwable t) { 942 if (log.isServiceEnabled()) { 943 log.service("Not valid because cast-to-validate threw exception " + t.getClass(), t); 944 } 945 return false; 946 } 947 } 948 949 protected abstract boolean simpleValid(Object v, Node node, Field field); 950 951 protected final void inherit(StaticAbstractRestriction source, boolean cast) { 952 Serializable inheritedValue = source.getValue(); 954 if (cast) inheritedValue = (Serializable) parent.cast(inheritedValue, null, null); 955 setValue(inheritedValue); 956 enforceStrength = source.getEnforceStrength(); 957 errorDescription = (LocalizedString) source.getErrorDescription().clone(); 958 } 959 960 protected final void inherit(StaticAbstractRestriction source) { 961 inherit(source, false); 962 } 963 964 public int getEnforceStrength() { 965 return enforceStrength; 966 } 967 968 public void setEnforceStrength(int e) { 969 enforceStrength = e; 970 } 971 972 public final String toString() { 973 return toString(null, null); 974 } 975 976 public final String toString(Node node, Field field) { 977 return name + ": " + 978 (enforceStrength == DataType.ENFORCE_NEVER ? "*" : "") + 979 valueString(node, field) + ( fixed ? "." : ""); 980 } 981 982 } 983 984 protected class RequiredRestriction extends AbstractRestriction { 986 private static final long serialVersionUID = 1L; 987 988 RequiredRestriction(RequiredRestriction source) { 989 super(source); 990 } 991 992 RequiredRestriction(boolean b) { 993 super("required", Boolean.valueOf(b)); 994 } 995 996 final boolean isRequired() { 997 return Boolean.TRUE.equals(value); 998 } 999 1000 protected boolean simpleValid(Object v, Node node, Field field) { 1001 if(!isRequired()) return true; 1002 return v != null; 1003 } 1004 } 1005 1006 protected class UniqueRestriction extends AbstractRestriction { 1008 private static final long serialVersionUID = 1L; 1009 UniqueRestriction(UniqueRestriction source) { 1010 super(source); 1011 } 1012 1013 UniqueRestriction(boolean b) { 1014 super("unique", Boolean.valueOf(b)); 1015 } 1016 1017 final boolean isUnique() { 1018 return Boolean.TRUE.equals(value); 1019 } 1020 1021 protected boolean simpleValid(Object v, Node node, Field field) { 1022 if (! isUnique()) return true; 1023 if (node != null && field != null && v != null && value != null ) { 1024 1025 if (field.isVirtual()) return true; 1027 if (!node.isNew()) { 1028 if (field.getName().equals("number")) { 1029 if (Casting.toInt(v) == node.getNumber()) { 1032 return true; 1033 } else { 1034 log.warn("Odd, changing number of node " + node + " ?!", new Exception ()); 1036 } 1037 } 1038 } 1039 1040 NodeManager nodeManager = field.getNodeManager(); 1041 Cloud cloud = nodeManager.getCloud(); 1042 if (cloud.getUser().getRank().getInt() < Rank.ADMIN_INT) { 1043 Cloud adminCloud = cloud.getCloudContext().getCloud("mmbase", "class", null); 1047 if (adminCloud.getUser().getRank().getInt() > cloud.getUser().getRank().getInt()) { 1048 cloud = adminCloud; 1049 nodeManager = adminCloud.getNodeManager(nodeManager.getName()); 1050 } 1051 } 1052 NodeQuery query = nodeManager.createQuery(); 1054 Constraint constraint = Queries.createConstraint(query, field.getName(), FieldCompareConstraint.EQUAL, v); 1055 Queries.addConstraint(query, constraint); 1056 if (!node.isNew()) { 1057 constraint = Queries.createConstraint(query, "number", FieldCompareConstraint.NOT_EQUAL, new Integer (node.getNumber())); 1058 Queries.addConstraint(query, constraint); 1059 } 1060 if(log.isDebugEnabled()) { 1061 log.debug(query); 1062 } 1063 return Queries.count(query) == 0; 1064 } else { 1065 return true; 1067 } 1068 } 1069 } 1070 1071 1072 1074 protected class TypeRestriction extends AbstractRestriction { 1075 private static final long serialVersionUID = 1L; 1076 TypeRestriction(TypeRestriction source) { 1077 super(source); 1078 } 1079 1080 TypeRestriction() { 1081 super("type", BasicDataType.this.getClass()); 1082 } 1083 1084 protected boolean simpleValid(Object v, Node node, Field field) { 1085 try { 1086 BasicDataType.this.cast(v, node, field); 1087 return true; 1088 } catch (Throwable e) { 1089 return false; 1090 } 1091 } 1092 } 1093 1094 protected class EnumerationRestriction extends AbstractRestriction { 1096 private static final long serialVersionUID = 1L; 1097 1098 EnumerationRestriction(EnumerationRestriction source) { 1099 super(source); 1100 value = value != null ? (Serializable) ((LocalizedEntryListFactory) value).clone() : null; 1101 } 1102 1103 EnumerationRestriction(LocalizedEntryListFactory entries) { 1104 super("enumeration", entries); 1105 } 1106 1107 final LocalizedEntryListFactory getEnumerationFactory() { 1108 if(value == null) { 1109 value = new LocalizedEntryListFactory(); 1110 } 1111 return (LocalizedEntryListFactory) value; 1112 } 1113 1114 public Collection getEnumeration(Locale locale, Cloud cloud, Node node, Field field) { 1115 if (value == null) return Collections.EMPTY_LIST; 1116 LocalizedEntryListFactory ef = (LocalizedEntryListFactory) value; 1117 if (cloud == null) { 1118 if (node != null) { 1119 cloud = node.getCloud(); 1120 } else if (field != null) { 1121 cloud = field.getNodeManager().getCloud(); 1122 } 1123 } 1124 return ef.get(locale, cloud); 1125 } 1126 1127 1130 protected Object preCast(Object v, Cloud cloud) { 1131 if (getValue() == null) return v; 1132 try { 1133 return ((LocalizedEntryListFactory) value).castKey(v, cloud); 1134 } catch (NoClassDefFoundError ncdfe) { 1136 log.error("Could not find class " + ncdfe.getMessage() + " while casting " + v.getClass() + " " + v, ncdfe); 1137 return v; 1138 } 1139 1140 } 1141 1142 protected boolean simpleValid(Object v, Node node, Field field) { 1143 if (value == null || ((LocalizedEntryListFactory) value).isEmpty()) { 1144 return true; 1145 } 1146 Cloud cloud = BasicDataType.this.getCloud(node, field); 1147 Collection validValues = getEnumeration(null, cloud, node, field); 1148 if (validValues.size() == 0) { 1149 return true; 1150 } 1151 Object candidate; 1152 try { 1153 candidate = BasicDataType.this.cast(v, cloud, node, field); 1154 } catch (CastException ce) { 1155 return false; 1156 } 1157 Iterator i = validValues.iterator(); 1158 while (i.hasNext()) { 1159 Map.Entry e = (Map.Entry) i.next(); 1160 Object valid = e.getKey(); 1161 if (valid.equals(candidate)) { 1162 return true; 1163 } 1164 } 1165 return false; 1166 } 1167 1168 protected String valueString(Node node, Field field) { 1169 Collection col = getEnumeration(null, null, node, field); 1170 if(col.size() == 0) return ""; 1171 StringBuffer buf = new StringBuffer (); 1172 Iterator it = col.iterator(); 1173 int i = 0; 1174 while (it.hasNext() && ++i < 10) { 1175 Map.Entry ent = (Map.Entry)it.next(); 1176 buf.append(Casting.toString(ent)); 1177 if (it.hasNext()) buf.append(", "); 1178 } 1179 if (i < col.size()) buf.append(".(" + (col.size() - i) + " more .."); 1180 return buf.toString(); 1181 } 1182 1183 } 1184 1185 1186 1187 1191 1193 protected class RestrictedEnumerationIterator implements Iterator { 1194 private final Iterator baseIterator; 1195 private final Node node; 1196 private final Field field; 1197 private Map.Entry next = null; 1198 1199 RestrictedEnumerationIterator(Locale locale, Cloud cloud, Node node, Field field) { 1200 Collection col = enumerationRestriction.getEnumeration(locale, cloud, node, field); 1201 if (log.isDebugEnabled()) { 1202 log.debug("Restricted iterator on " + col); 1203 } 1204 baseIterator = col.iterator(); 1205 this.node = node; 1206 this.field = field; 1207 determineNext(); 1208 } 1209 1210 protected void determineNext() { 1211 next = null; 1212 while (baseIterator.hasNext()) { 1213 final Map.Entry entry = (Map.Entry) baseIterator.next(); 1214 Object value = entry.getKey(); 1215 Collection validationResult = BasicDataType.this.validate(value, node, field, false); 1216 if (validationResult == VALID) { 1217 next = entry; 1218 1231 break; 1232 } else if (log.isDebugEnabled()) { 1233 String errors = ""; 1234 for (Iterator i = validationResult.iterator(); i.hasNext();) { 1235 errors += ((LocalizedString)i.next()).get(null); 1236 } 1237 log.debug("Value " + value.getClass() + " " + value + " does not validate : " + errors); 1238 } 1239 } 1240 } 1241 1242 public boolean hasNext() { 1243 return next != null; 1244 } 1245 1246 public Object next() { 1247 if (next == null) { 1248 throw new NoSuchElementException(); 1249 } 1250 Object n = next; 1251 determineNext(); 1252 return n; 1253 } 1254 1255 public void remove() { 1256 throw new UnsupportedOperationException ("Cannot remove entries from " + getClass()); 1257 } 1258 1259 public String toString() { 1260 return "restricted iterator(" + enumerationRestriction + ")"; 1261 } 1262 } 1263 1264} 1265 | Popular Tags |