1 21 package au.id.jericho.lib.html; 22 23 import java.util.*; 24 25 70 public abstract class FormControl extends Segment { 71 FormControlType formControlType; 72 String name; 73 ElementContainer elementContainer; 74 FormControlOutputStyle outputStyle=FormControlOutputStyle.NORMAL; 75 76 private static final String CHECKBOX_NULL_DEFAULT_VALUE="on"; 77 private static Comparator COMPARATOR=new PositionComparator(); 78 79 static FormControl construct(final Element element) { 80 final String tagName=element.getStartTag().getName(); 81 if (tagName==Tag.INPUT) { 82 final String typeAttributeValue=element.getAttributes().getRawValue(Attribute.TYPE); 83 if (typeAttributeValue==null) return new InputFormControl(element,FormControlType.TEXT); 84 FormControlType formControlType=FormControlType.getFromInputElementType(typeAttributeValue.toLowerCase()); 85 if (formControlType==null) { 86 if (element.source.isLoggingEnabled()) element.source.log(element.source.getRowColumnVector(element.begin).appendTo(new StringBuffer (200)).append(": INPUT control with unrecognised type \"").append(typeAttributeValue).append("\" assumed to be type \"text\"").toString()); 87 formControlType=FormControlType.TEXT; 88 } 89 if (formControlType==FormControlType.TEXT) return new InputFormControl(element,formControlType); 90 if (formControlType==FormControlType.CHECKBOX || formControlType==FormControlType.RADIO) return new RadioCheckboxFormControl(element,formControlType); 91 if (formControlType==FormControlType.SUBMIT) return new SubmitFormControl(element,formControlType); 92 if (formControlType==FormControlType.IMAGE) return new ImageSubmitFormControl(element); 93 return new InputFormControl(element,formControlType); 95 } else if (tagName==Tag.SELECT) { 96 return new SelectFormControl(element); 97 } else if (tagName==Tag.TEXTAREA) { 98 return new TextAreaFormControl(element); 99 } else if (tagName==Tag.BUTTON) { 100 return element.getAttributes().getRawValue(Attribute.TYPE).equalsIgnoreCase("submit") ? new SubmitFormControl(element,FormControlType.BUTTON) : null; 101 } else { 102 return null; 103 } 104 } 105 106 private FormControl(final Element element, final FormControlType formControlType, final boolean loadPredefinedValue) { 107 super(element.source,element.begin,element.end); 108 elementContainer=new ElementContainer(element,loadPredefinedValue); 109 this.formControlType=formControlType; 110 name=element.getAttributes().getValue(Attribute.NAME); 111 verifyName(); 112 } 113 114 118 public final FormControlType getFormControlType() { 119 return formControlType; 120 } 121 122 137 public final String getName() { 138 return name; 139 } 140 141 151 public final Element getElement() { 152 return elementContainer.element; 153 } 154 155 164 public Iterator getOptionElementIterator() { 165 throw new UnsupportedOperationException ("Only SELECT controls contain OPTION elements"); 167 } 168 169 181 public FormControlOutputStyle getOutputStyle() { 182 return outputStyle; 183 } 184 185 192 public void setOutputStyle(final FormControlOutputStyle outputStyle) { 193 this.outputStyle=outputStyle; 194 } 195 196 224 public final Map getAttributesMap() { 225 return elementContainer.getAttributesMap(); 226 } 227 228 240 public final boolean isDisabled() { 241 return elementContainer.getBooleanAttribute(Attribute.DISABLED); 242 } 243 244 258 public final void setDisabled(final boolean disabled) { 259 elementContainer.setBooleanAttribute(Attribute.DISABLED,disabled); 260 } 261 262 295 public boolean isChecked() { 296 throw new UnsupportedOperationException ("This property is only relevant for CHECKBOX and RADIO controls"); 297 } 298 299 331 public String getPredefinedValue() { 332 return elementContainer.predefinedValue; 333 } 334 335 366 public Collection getPredefinedValues() { 367 return getPredefinedValue()!=null ? Collections.singleton(getPredefinedValue()) : Collections.EMPTY_SET; 368 } 369 370 434 public Collection getValues() { 435 final HashSet values=new HashSet(); 436 addValuesTo(values); 437 return values; 438 } 439 440 450 public final void clearValues() { 451 setValue(null); 452 } 453 454 492 public abstract boolean setValue(CharSequence value); 493 494 515 public boolean addValue(final CharSequence value) { 516 return setValue(value); 517 } 518 519 abstract void addValuesTo(Collection collection); abstract void addToFormFields(FormFields formFields); 521 abstract void replaceInOutputDocument(OutputDocument outputDocument); 522 523 public String getDebugInfo() { 524 final StringBuffer sb=new StringBuffer (); 525 sb.append(formControlType).append(" name=\"").append(name).append('"'); 526 if (elementContainer.predefinedValue!=null) sb.append(" PredefinedValue=\"").append(elementContainer.predefinedValue).append('"'); 527 sb.append(" - ").append(getElement().getDebugInfo()); 528 return sb.toString(); 529 } 530 531 static final class InputFormControl extends FormControl { 532 public InputFormControl(final Element element, final FormControlType formControlType) { 534 super(element,formControlType,false); 535 } 536 public boolean setValue(final CharSequence value) { 537 elementContainer.setAttributeValue(Attribute.VALUE,value); 538 return true; 539 } 540 void addValuesTo(final Collection collection) { 541 addValueTo(collection,elementContainer.getAttributeValue(Attribute.VALUE)); 542 } 543 void addToFormFields(final FormFields formFields) { 544 formFields.add(this); 545 } 546 void replaceInOutputDocument(final OutputDocument outputDocument) { 547 if (outputStyle==FormControlOutputStyle.REMOVE) { 548 outputDocument.remove(getElement()); 549 } else if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) { 550 String output=null; 551 if (formControlType!=FormControlType.HIDDEN) { 552 CharSequence value=elementContainer.getAttributeValue(Attribute.VALUE); 553 if (formControlType==FormControlType.PASSWORD && value!=null) value=getString(FormControlOutputStyle.ConfigDisplayValue.PasswordChar,value.length()); 554 output=getDisplayValueHTML(value,false); 555 } 556 outputDocument.replace(getElement(),output); 557 } else { 558 replaceAttributesInOutputDocumentIfModified(outputDocument); 559 } 560 } 561 } 562 563 static final class TextAreaFormControl extends FormControl { 564 public CharSequence value=UNCHANGED; 566 private static final String UNCHANGED=new String (); 567 public TextAreaFormControl(final Element element) { 568 super(element,FormControlType.TEXTAREA,false); 569 } 570 public boolean setValue(final CharSequence value) { 571 this.value=value; 572 return true; 573 } 574 void addValuesTo(final Collection collection) { 575 addValueTo(collection,getValue()); 576 } 577 void addToFormFields(final FormFields formFields) { 578 formFields.add(this); 579 } 580 void replaceInOutputDocument(final OutputDocument outputDocument) { 581 if (outputStyle==FormControlOutputStyle.REMOVE) { 582 outputDocument.remove(getElement()); 583 } else if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) { 584 outputDocument.replace(getElement(),getDisplayValueHTML(getValue(),true)); 585 } else { 586 replaceAttributesInOutputDocumentIfModified(outputDocument); 587 if (value!=UNCHANGED) 588 outputDocument.replace(getElement().getContent(),CharacterReference.encode(value)); 589 } 590 } 591 private CharSequence getValue() { 592 return (value==UNCHANGED) ? CharacterReference.decode(getElement().getContent()) : value; 593 } 594 } 595 596 static final class RadioCheckboxFormControl extends FormControl { 597 public RadioCheckboxFormControl(final Element element, final FormControlType formControlType) { 599 super(element,formControlType,true); 600 if (elementContainer.predefinedValue==null) { 601 elementContainer.predefinedValue=CHECKBOX_NULL_DEFAULT_VALUE; 602 if (element.source.isLoggingEnabled()) element.source.log(element.source.getRowColumnVector(element.begin).appendTo(new StringBuffer (200)).append(": compulsory \"value\" attribute of ").append(formControlType).append(" control \"").append(name).append("\" is missing, assuming the value \"").append(CHECKBOX_NULL_DEFAULT_VALUE).append('"').toString()); 603 } 604 } 605 public boolean setValue(final CharSequence value) { 606 return elementContainer.setSelected(value,Attribute.CHECKED,false); 607 } 608 public boolean addValue(final CharSequence value) { 609 return elementContainer.setSelected(value,Attribute.CHECKED,formControlType==FormControlType.CHECKBOX); 610 } 611 void addValuesTo(final Collection collection) { 612 if (isChecked()) addValueTo(collection,getPredefinedValue()); 613 } 614 public boolean isChecked() { 615 return elementContainer.getBooleanAttribute(Attribute.CHECKED); 616 } 617 void addToFormFields(final FormFields formFields) { 618 formFields.add(this); 619 } 620 void replaceInOutputDocument(final OutputDocument outputDocument) { 621 if (outputStyle==FormControlOutputStyle.REMOVE) { 622 outputDocument.remove(getElement()); 623 } else { 624 if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) { 625 final String html=isChecked() ? FormControlOutputStyle.ConfigDisplayValue.CheckedHTML : FormControlOutputStyle.ConfigDisplayValue.UncheckedHTML; 626 if (html!=null) { 627 outputDocument.replace(getElement(),html); 628 return; 629 } 630 setDisabled(true); 631 } 632 replaceAttributesInOutputDocumentIfModified(outputDocument); 633 } 634 } 635 } 636 637 static class SubmitFormControl extends FormControl { 638 public SubmitFormControl(final Element element, final FormControlType formControlType) { 640 super(element,formControlType,true); 641 } 642 public boolean setValue(final CharSequence value) { 643 return false; 644 } 645 void addValuesTo(final Collection collection) {} 646 void addToFormFields(final FormFields formFields) { 647 if (getPredefinedValue()!=null) formFields.add(this); 648 } 649 void replaceInOutputDocument(final OutputDocument outputDocument) { 650 if (outputStyle==FormControlOutputStyle.REMOVE) { 651 outputDocument.remove(getElement()); 652 } else { 653 if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) setDisabled(true); 654 replaceAttributesInOutputDocumentIfModified(outputDocument); 655 } 656 } 657 } 658 659 static final class ImageSubmitFormControl extends SubmitFormControl { 660 public ImageSubmitFormControl(final Element element) { 662 super(element,FormControlType.IMAGE); 663 } 664 void addToFormFields(final FormFields formFields) { 665 super.addToFormFields(formFields); 666 formFields.addName(this,name+".x"); 667 formFields.addName(this,name+".y"); 668 } 669 } 670 671 static final class SelectFormControl extends FormControl { 672 public ElementContainer[] optionElementContainers; 674 public SelectFormControl(final Element element) { 675 super(element,element.getAttributes().get(Attribute.MULTIPLE)!=null ? FormControlType.SELECT_MULTIPLE : FormControlType.SELECT_SINGLE,false); 676 final List optionElements=element.findAllElements(Tag.OPTION); 677 optionElementContainers=new ElementContainer[optionElements.size()]; 678 int x=0; 679 for (final Iterator i=optionElements.iterator(); i.hasNext();) { 680 final ElementContainer optionElementContainer=new ElementContainer((Element)i.next(),true); 681 if (optionElementContainer.predefinedValue==null) 682 optionElementContainer.predefinedValue=CharacterReference.decodeCollapseWhiteSpace(optionElementContainer.element.getContent()); 684 optionElementContainers[x++]=optionElementContainer; 685 } 686 } 687 public String getPredefinedValue() { 688 throw new UnsupportedOperationException ("Use getPredefinedValues() method instead on SELECT controls"); 689 } 690 public Collection getPredefinedValues() { 691 final ArrayList arrayList=new ArrayList(optionElementContainers.length); 692 for (int i=0; i<optionElementContainers.length; i++) 693 arrayList.add(optionElementContainers[i].predefinedValue); 694 return arrayList; 695 } 696 public Iterator getOptionElementIterator() { 697 return new OptionElementIterator(); 698 } 699 public boolean setValue(final CharSequence value) { 700 return addValue(value,false); 701 } 702 public boolean addValue(final CharSequence value) { 703 return addValue(value,formControlType==FormControlType.SELECT_MULTIPLE); 704 } 705 private boolean addValue(final CharSequence value, final boolean allowMultipleValues) { 706 boolean valueFound=false; 707 for (int i=0; i<optionElementContainers.length; i++) { 708 if (optionElementContainers[i].setSelected(value,Attribute.SELECTED,allowMultipleValues)) valueFound=true; 709 } 710 return valueFound; 711 } 712 void addValuesTo(final Collection collection) { 713 for (int i=0; i<optionElementContainers.length; i++) { 714 if (optionElementContainers[i].getBooleanAttribute(Attribute.SELECTED)) 715 addValueTo(collection,optionElementContainers[i].predefinedValue); 716 } 717 } 718 void addToFormFields(final FormFields formFields) { 719 for (int i=0; i<optionElementContainers.length; i++) 720 formFields.add(this,optionElementContainers[i].predefinedValue); 721 } 722 void replaceInOutputDocument(final OutputDocument outputDocument) { 723 if (outputStyle==FormControlOutputStyle.REMOVE) { 724 outputDocument.remove(getElement()); 725 } else if (outputStyle==FormControlOutputStyle.DISPLAY_VALUE) { 726 final StringBuffer sb=new StringBuffer (100); 727 for (int i=0; i<optionElementContainers.length; i++) { 728 if (optionElementContainers[i].getBooleanAttribute(Attribute.SELECTED)) { 729 appendCollapseWhiteSpace(sb,optionElementContainers[i].element.getContent()); 730 sb.append(FormControlOutputStyle.ConfigDisplayValue.MultipleValueSeparator); 731 } 732 } 733 if (sb.length()>0) sb.setLength(sb.length()-FormControlOutputStyle.ConfigDisplayValue.MultipleValueSeparator.length()); outputDocument.replace(getElement(),getDisplayValueHTML(sb,false)); 735 } else { 736 replaceAttributesInOutputDocumentIfModified(outputDocument); 737 for (int i=0; i<optionElementContainers.length; i++) { 738 optionElementContainers[i].replaceAttributesInOutputDocumentIfModified(outputDocument); 739 } 740 } 741 } 742 private final class OptionElementIterator implements Iterator { 743 private int i=0; 744 public boolean hasNext() { 745 return i<optionElementContainers.length; 746 } 747 public Object next() { 748 if (!hasNext()) throw new NoSuchElementException(); 749 return optionElementContainers[i++].element; 750 } 751 public void remove() { 752 throw new UnsupportedOperationException (); 753 } 754 } 755 } 756 757 final String getDisplayValueHTML(final CharSequence text, final boolean whiteSpaceFormatting) { 758 final StringBuffer sb=new StringBuffer ((text==null ? 0 : text.length()*2)+50); 759 sb.append('<').append(FormControlOutputStyle.ConfigDisplayValue.ElementName); 760 for (final Iterator i=FormControlOutputStyle.ConfigDisplayValue.AttributeNames.iterator(); i.hasNext();) { 761 final String attributeName=i.next().toString(); 762 final CharSequence attributeValue=elementContainer.getAttributeValue(attributeName); 763 if (attributeValue==null) continue; 764 Attribute.appendHTML(sb,attributeName,attributeValue); 765 } 766 sb.append('>'); 767 if (text==null || text.length()==0) 768 sb.append(FormControlOutputStyle.ConfigDisplayValue.EmptyHTML); 769 else 770 CharacterReference.appendEncode(sb,text,whiteSpaceFormatting); 771 sb.append(EndTagType.START_DELIMITER_PREFIX).append(FormControlOutputStyle.ConfigDisplayValue.ElementName).append('>'); 772 return sb.toString(); 773 } 774 775 final void replaceAttributesInOutputDocumentIfModified(final OutputDocument outputDocument) { 776 elementContainer.replaceAttributesInOutputDocumentIfModified(outputDocument); 777 } 778 779 static List findAll(final Segment segment) { 780 final ArrayList list=new ArrayList(); 781 findAll(segment,list,Tag.INPUT); 782 findAll(segment,list,Tag.TEXTAREA); 783 findAll(segment,list,Tag.SELECT); 784 findAll(segment,list,Tag.BUTTON); 785 Collections.sort(list,COMPARATOR); 786 return list; 787 } 788 789 private static void findAll(final Segment segment, final ArrayList list, final String tagName) { 790 for (final Iterator i=segment.findAllElements(tagName).iterator(); i.hasNext();) 791 list.add(((Element)i.next()).getFormControl()); 792 } 793 794 private static CharSequence getString(final char ch, final int length) { 795 if (length==0) return ""; 796 final StringBuffer sb=new StringBuffer (length); 797 for (int i=0; i<length; i++) sb.append(ch); 798 return sb.toString(); 799 } 800 801 private void verifyName() { 802 if (formControlType.isSubmit()) return; 803 String missingOrBlank; 804 if (name==null) { 805 missingOrBlank="missing"; 806 } else { 807 if (name.length()!=0) return; 808 missingOrBlank="blank"; 809 } 810 final Source source=getElement().source; 811 if (source.isLoggingEnabled()) source.log(getElement().source.getRowColumnVector(getElement().begin).appendTo(new StringBuffer (200)).append(": compulsory \"name\" attribute of ").append(formControlType).append(" control is ").append(missingOrBlank).toString()); 812 } 813 814 private static final void addValueTo(final Collection collection, final CharSequence value) { 815 collection.add(value!=null ? value : ""); 816 } 817 818 private static final class PositionComparator implements Comparator { 819 public int compare(final Object o1, final Object o2) { 820 int formControl1Begin=((FormControl)o1).getElement().getBegin(); 821 int formControl2Begin=((FormControl)o2).getElement().getBegin(); 822 if (formControl1Begin<formControl2Begin) return -1; 823 if (formControl1Begin>formControl2Begin) return 1; 824 return 0; 825 } 826 } 827 828 830 static final class ElementContainer { 831 public final Element element; 834 public Map attributesMap=null; 835 public String predefinedValue; 837 public ElementContainer(final Element element, final boolean loadPredefinedValue) { 838 this.element=element; 839 predefinedValue=loadPredefinedValue ? element.getAttributes().getValue(Attribute.VALUE) : null; 840 } 841 842 public Map getAttributesMap() { 843 if (attributesMap==null) attributesMap=element.getAttributes().getMap(true); 844 return attributesMap; 845 } 846 847 public boolean setSelected(final CharSequence value, final String selectedOrChecked, final boolean allowMultipleValues) { 848 if (value!=null && predefinedValue.equals(value.toString())) { 849 setBooleanAttribute(selectedOrChecked,true); 850 return true; 851 } 852 if (!allowMultipleValues) setBooleanAttribute(selectedOrChecked,false); 853 return false; 854 } 855 856 public CharSequence getAttributeValue(final String attributeName) { 857 if (attributesMap!=null) 858 return (CharSequence )attributesMap.get(attributeName); 859 else 860 return element.getAttributes().getValue(attributeName); 861 } 862 863 public void setAttributeValue(final String attributeName, final CharSequence value) { 864 if (value==null) { 866 setBooleanAttribute(attributeName,false); 867 return; 868 } 869 if (attributesMap!=null) { 870 attributesMap.put(attributeName,value); 871 return; 872 } 873 final String valueString=value.toString(); 874 final CharSequence existingValue=getAttributeValue(attributeName); 875 if (existingValue!=null && existingValue.toString().equals(valueString)) return; 876 getAttributesMap().put(attributeName,valueString); 877 } 878 879 public boolean getBooleanAttribute(final String attributeName) { 880 if (attributesMap!=null) 881 return attributesMap.containsKey(attributeName); 882 else 883 return element.getAttributes().get(attributeName)!=null; 884 } 885 886 public void setBooleanAttribute(final String attributeName, final boolean value) { 887 final boolean oldValue=getBooleanAttribute(attributeName); 888 if (value==oldValue) return; 889 if (value) 890 getAttributesMap().put(attributeName,attributeName); else 892 getAttributesMap().remove(attributeName); 893 } 894 895 public void replaceAttributesInOutputDocumentIfModified(final OutputDocument outputDocument) { 896 if (attributesMap!=null) outputDocument.replace(element.getAttributes(),attributesMap); 897 } 898 } 899 } 900 901 | Popular Tags |