function AbstractComponent( id, componentsArray ) {
    if( arguments.length > 0 ) {
        this.init( id, componentsArray );
    }
};

AbstractComponent.prototype.init = function( id, componentsArray ) {

    this.id = id;
    this.components = new Array();

    if( componentsArray != null && componentsArray.length > 0 ) {
        for( var index = 0; index < componentsArray.length; index++) {
            var nextComponent = componentsArray[ index ];
            if( nextComponent != null ) {
                this.addComponent( nextComponent );
            }
        }
    }
};

AbstractComponent.prototype.getId = function() {
    return this.id;
};
AbstractComponent.prototype.setId = function( id ) {
    this.id = id;
};

AbstractComponent.prototype.getComponentById = function( id ) {
    var component = null;
    var componentIndex = this.getComponentIndexById( id );
    if( componentIndex != -1 ) {
        component = this.components[ componentIndex ];
    }
    return component;
};

AbstractComponent.prototype.getComponentIndexById = function( id ) {
    var componentIndex = -1;

    if( id != null && id.length > 0 ) {

        for( var index = 0; index < this.components.length && componentIndex == -1 ; index++ ) {
            if( this.components[ index ].getId() == id ) {
                componentIndex = index;
            }
        }
    }

    return componentIndex;
};

AbstractComponent.prototype.addComponent = function( component ) {
    if( component != null ) {
        this.components.push( component );
    }
};
AbstractComponent.prototype.removeComponent = function( component ) {
   if( component != null ) {
        var index = this.getComponentIndexById( component.getId() );
        if( index != -1 ) {
            this.components.splice( index,  1 );
        }
   }
};

AbstractComponent.prototype.getComponents = function() {
    return this.components;
};

AbstractComponent.prototype.Accept = function( visitor ) {
    visitor.visitAbstract( this );
};

ComponentFormNode.prototype = new AbstractComponent();
ComponentFormNode.prototype.constructor = ComponentFormNode;
ComponentFormNode.superclass = AbstractComponent.prototype;

function ComponentFormNode( formNode ) {
    if ( arguments.length > 0 ) {
        this.init( formNode );
    }
};

ComponentFormNode.prototype.init = function( formNode ) {
    ComponentFormNode.superclass.init.call( this, formNode.getName() );
    this.formNode = formNode;
};

ComponentFormNode.prototype.Accept = function( visitor ) {
    visitor.visitFormNode( this );
};

ComponentFormNode.prototype.getFormNode = function() {
    return this.formNode;
};

function createFormNodeComponent( formNode, formNodeName ) {
    var formNodeComponent = null;
    if( formNode != null && formNodeName != null ) {

        var formNodes = getNodesWithNameAndAttributeValue( formNode, FormNodeMetadata.ELEMENT_SELECT, "name", formNodeName );
        if( formNodes == null || formNodes.length == 0 ) {
            formNodes = getNodesWithNameAndAttributeValue( formNode, FormNodeMetadata.ELEMENT_INPUT, "name", formNodeName );
        }

        if( formNodes != null && formNodes.length == 1 ) {

            formNodeComponent = new ComponentFormNode( formNodes[0] );
        }

    }
    return formNodeComponent;
};

ComponentFormNodeGroup.prototype = new AbstractComponent();
ComponentFormNodeGroup.prototype.constructor = ComponentFormNodeGroup;
ComponentFormNodeGroup.superclass = AbstractComponent.prototype;

function ComponentFormNodeGroup( id, formNodes ) {
    if ( arguments.length > 0 ) {
        this.init( id, formNodes );
    }
};

ComponentFormNodeGroup.prototype.init = function( id, formNodes ) {
    ComponentFormNodeGroup.superclass.init.call( this, id );
    if( formNodes != null && formNodes.length > 0 ) {
        for( var index = 0; index < formNodes.length; index++ ) {
            this.addComponent( new ComponentFormNode( formNodes[index] ) );
        }
    }
};

ComponentFormNodeGroup.prototype.Accept = function( visitor ) {
    visitor.visitFormNodeGroup( this );
};

function createGroup( formNode, groupName, expectedValues ) {
    var group = null;

    if( formNode != null ) {

        var inputNodes = getNodesWithNameAndAttributeValue( formNode, FormNodeMetadata.ELEMENT_INPUT, "name", groupName );
        var groupNodes = new Array();
        for( var index=0; index < inputNodes.length; index++ ) {

            var nextNode = new FormNode( inputNodes[ index ] );

            if( arrayContains( expectedValues, nextNode.getValue() ) ) {
                groupNodes.push( nextNode );
            }
        }

        group = new ComponentFormNodeGroup( groupName, groupNodes );
    }
    return group;
};

ComponentHierachy.prototype = new ComponentFormNode();
ComponentHierachy.prototype.constructor = ComponentHierachy;
ComponentHierachy.superclass = ComponentFormNode.prototype;

function ComponentHierachy( formNode, formNodeChilds, parentComponent ) {
    if ( arguments.length > 0 ) {
        this.init( formNode, formNodeChilds, parentComponent );
    }
};

ComponentHierachy.prototype.init = function( formNode, formNodeChilds, parentComponent ) {

    ComponentHierachy.superclass.init.call( this, formNode );
    this.parent = parentComponent;
    if( formNodeChilds != null && formNodeChilds.length > 0 ) {

        for( var index = 0; index < formNodeChilds.length ; index++ ) {
            var nextChild = new ComponentHierachy( formNodeChilds[ index ], new Array(), this );
            this.addComponent( nextChild );
        }

    }
};

ComponentHierachy.prototype.addComponent = function( childComponent ) {
    if( childComponent != null ) {
        ComponentHierachy.superclass.addComponent.call( this, childComponent );
        childComponent.setParent( this );
    }
};

ComponentHierachy.prototype.setParent = function( parent ) {
    this.parent = parent;
};
ComponentHierachy.prototype.getParent = function() {
    return this.parent;
};

ComponentHierachy.prototype.Accept = function( visitor ) {
    visitor.visitHierachy( this );
};

ComponentHierachy.prototype.isLeaf = function() {
    return this.getComponents() == null || this.getComponents().length == 0;
};

ComponentHierachy.prototype.isRoot = function() {
    return this.parent == null;
};

ComponentHierachy.prototype.getRoot = function() {
    var root = null;
    if( this.isRoot() ) {
        root = this;
    } else {
        root = this.getParent().getRoot();
    }
    return root;
};

ComponentHierachy.prototype.findLeaf = function( componentValue ) {
    var child = null;

    if( componentValue != null ) {

        if( this.getFormNode().getValue() == componentValue ) {
            child = this;
        } else {

          var childs = this.getComponents();
          for( var childIndex = 0; childIndex < childs.length && child == null ; childIndex++ ) {
              var nextChild = childs[ childIndex ];
              child = nextChild.findLeaf( componentValue );
          }
        }

    }

    return child;
};

ComponentDate.prototype = new AbstractComponent();
ComponentDate.prototype.constructor = ComponentDate;
ComponentDate.superclass = AbstractComponent.prototype;

function ComponentDate( id, formNodeDay, formNodeMonth, formNodeYear, monthCorrection ) {
    if ( arguments.length > 0 ) {
        this.init( id, formNodeDay, formNodeMonth, formNodeYear, monthCorrection );
    }
};

ComponentDate.prototype.init = function( id, formNodeDay, formNodeMonth, formNodeYear, monthCorrection ) {

    this.dayId = formNodeDay.getName();
    this.monthId = formNodeMonth.getName();
    this.yearId = formNodeYear.getName();
    if( monthCorrection != null ) {
        this.monthCorrection = monthCorrection;
    } else {
        this.monthCorrection = monthCorrection;
    }

    ComponentDate.superclass.init.call( this, id , new Array( new ComponentFormNode( formNodeDay ), new ComponentFormNode( formNodeMonth ), new ComponentFormNode( formNodeYear ) ) );
};

ComponentDate.prototype.getDayComponent = function() {
    return this.getComponentById( this.dayId );
};
ComponentDate.prototype.getMonthComponent = function() {
    return this.getComponentById( this.monthId );
};
ComponentDate.prototype.getYearComponent = function() {
    return this.getComponentById( this.yearId );
};

ComponentDate.prototype.getSelectedDay = function() {
    return this.getDayComponent().getFormNode().getSelectedValue();
};
ComponentDate.prototype.getSelectedMonth = function() {
    return Number( this.getMonthComponent().getFormNode().getSelectedValue() ) + this.monthCorrection;
    //return this.getMonthComponent().getFormNode().getSelectedValue();
};
ComponentDate.prototype.getSelectedYear = function() {
    return this.getYearComponent().getFormNode().getSelectedValue();
};

ComponentDate.prototype.hasSelectedYear = function() {
    return this.getYearComponent().getFormNode().hasAnySelectedValue();
};
ComponentDate.prototype.hasSelectedMonth = function() {
    return this.getMonthComponent().getFormNode().hasAnySelectedValue();
};
ComponentDate.prototype.hasSelectedDay = function() {
    return this.getDayComponent().getFormNode().hasAnySelectedValue();
};


ComponentDate.prototype.getSelectedDate = function() {
    return new Date( this.getSelectedYear(), this.getSelectedMonth(), this.getSelectedDay() );
};

ComponentDate.prototype.setSelectedDate = function( selectedDate ) {

    if( selectedDate != null ) {

        //var oldDate = this.getSelectedDate();

        //alert( "Setting date to: " + selectedDate + " Correction = " + this.monthCorrection );
        //this.getYearComponent().getFormNode().removeSelectedValue( this.getSelectedYear() );
        this.getYearComponent().getFormNode().setSelectedValue( selectedDate.getFullYear() );

        //this.getMonthComponent().getFormNode().removeSelectedValue( this.getSelectedMonth() );
        this.getMonthComponent().getFormNode().setSelectedValue( selectedDate.getMonth() - this.monthCorrection );
        //this.getMonthComponent().getFormNode().setSelectedValue( selectedDate.getMonth() );

        //this.getDayComponent().getFormNode().removeSelectedValue( this.getSelectedDay() );
        this.getDayComponent().getFormNode().setSelectedValue( selectedDate.getDate() );

    }
};

ComponentDate.prototype.getMonthCorrection = function() {
    return this.monthCorrection;
}


ComponentDate.prototype.Accept = function( visitor ) {
    visitor.visitDate( this );
};

function createDateComponent( formNode, dayNodeName, monthNodeName, yearNodeName, componentName, monthCorrection ) {
    var dateComponent = null;
    if( formNode != null && dayNodeName != null && monthNodeName != null && yearNodeName != null ) {

        var yearNodes = getNodesWithNameAndAttributeValue( formNode, FormNodeMetadata.ELEMENT_SELECT, "name", yearNodeName );
        var monthNodes = getNodesWithNameAndAttributeValue( formNode, FormNodeMetadata.ELEMENT_SELECT, "name", monthNodeName );
        var dayNodes = getNodesWithNameAndAttributeValue( formNode, FormNodeMetadata.ELEMENT_SELECT, "name", dayNodeName );

        if( dayNodes != null && yearNodes != null && monthNodes != null &&
                dayNodes.length == 1 && monthNodes.length == 1 && yearNodes.length == 1 ) {

                dateComponent = new ComponentDate( componentName, new FormNode( dayNodes[0] ), new FormNode( monthNodes[0] ), new FormNode( yearNodes[0] ), monthCorrection );
        }

    }
    return dateComponent;
};

ComponentDateRange.prototype = new AbstractComponent();
ComponentDateRange.prototype.constructor = ComponentDateRange;
ComponentDateRange.superclass = AbstractComponent.prototype;

function ComponentDateRange( id, componentFromDate, componentToDate, componentDuration, modifyDurationOnly, maxDuration ) {
    if ( arguments.length > 0 ) {
        this.init( id, componentFromDate, componentToDate, componentDuration, modifyDurationOnly, maxDuration);
    }
};

ComponentDateRange.prototype.init = function( id, componentFromDate, componentToDate, componentDuration, modifyDurationOnly, maxDuration ) {

    ComponentDateRange.superclass.init.call( this, id , null );

    this.fromDateId = componentFromDate.getId();
    this.addComponent( componentFromDate );

    this.toDateId = componentToDate.getId();
    this.addComponent( componentToDate );

    if( componentDuration != null ) {
        this.durationId = componentDuration.getId();
        this.maxDuration = maxDuration;
        this.modifyDurationOnly = modifyDurationOnly;
        this.addComponent( componentDuration );
    }
    else {
        this.durationId = null;
    }
};

ComponentDateRange.prototype.getFromDateComponent = function() {
    return this.getComponentById( this.fromDateId );
};
ComponentDateRange.prototype.getToDateComponent = function() {
    return this.getComponentById( this.toDateId );
};
ComponentDateRange.prototype.getDurationComponent = function() {
    return this.getComponentById( this.durationId );
};
ComponentDateRange.prototype.hasDurationComponent = function() {
    return this.durationId != null;
};

ComponentDateRange.prototype.getSelectedDuration = function() {
    return this.getDurationComponent().getFormNode().getSelectedValue();
};
ComponentDateRange.prototype.setDuration = function( duration ) {
    this.getDurationComponent().getFormNode().setSelectedValue( duration );
};
ComponentDateRange.prototype.getDuration = function() {
    var duration = Number( this.getSelectedDuration() );
    if( isNaN( duration ) ) {
        duration = 0;
    }
    return duration;
};
ComponentDateRange.prototype.getDateRange = function() {
    return new DateRange( this.getFromDateComponent().getSelectedDate(), this.getToDateComponent().getSelectedDate() );
};

ComponentDateRange.prototype.Accept = function( visitor ) {
    visitor.visitDateRange( this, this.modifyDurationOnly, this.maxDuration );
};

function createDateRangeComponent(
    formNode, dateRangeComponentName,
    fromDayNodeName, fromMonthNodeName, fromYearNodeName,
    toDayNodeName, toMonthNodeName, toYearNodeName,
    durationNodeName,
    monthCorrection,
    modifyDurationOnly,
    maxDuration ) {

    var dateRangeComponent = null;

    var fromDate = createDateComponent( formNode, fromDayNodeName, fromMonthNodeName, fromYearNodeName, "FromDate", monthCorrection );
    var toDate = createDateComponent( formNode, toDayNodeName, toMonthNodeName, toYearNodeName, "ToDate", monthCorrection );

    if( fromDate != null && toDate != null ) {
        var durationComponent = createDurationComponent( formNode, durationNodeName );
        dateRangeComponent = new ComponentDateRange( dateRangeComponentName, fromDate, toDate, durationComponent, modifyDurationOnly, maxDuration );
    }

    return dateRangeComponent;
};
function createDurationComponent( formNode, durationNodeName ) {
    var durationComponent = null;
    if( durationNodeName != null ) {
        var durationNodes = getNodesWithNameAndAttributeValue( formNode, FormNodeMetadata.ELEMENT_INPUT, "name", durationNodeName );
        if( durationNodes == null || durationNodes.length == 0 ) {
            durationNodes = getNodesWithNameAndAttributeValue( formNode, FormNodeMetadata.ELEMENT_SELECT, "name", durationNodeName );
        }
        if( durationNodes != null && durationNodes.length == 1 ) {
            durationComponent = new ComponentFormNode( new FormNode( durationNodes[0] ) );
        }
    }
    return durationComponent;
}




function AbstractVisitor() {
};

AbstractVisitor.prototype.visitAbstract = function( abstractComponent ) {
    return false;
};
AbstractVisitor.prototype.visitFormNode = function( componentFormNode ) {
    return false;
};
AbstractVisitor.prototype.visitDate = function( componentDate ) {
    return false;
};
AbstractVisitor.prototype.visitDateRange = function( componentDateRange ) {
    return false;
};
AbstractVisitor.prototype.visitHierachy = function( componentHierachy ) {
    return false;
};

AbstractVisitor.prototype.visitFormNodeGroup = function( componentFormNodeGroup ) {
    return false;
};

VisitorDateMaxDayCorrection.prototype = new AbstractVisitor();
VisitorDateMaxDayCorrection.prototype.constructor = VisitorDateMaxDayCorrection;
VisitorDateMaxDayCorrection.superclass = AbstractVisitor.prototype;

function VisitorDateMaxDayCorrection() {
};


VisitorDateMaxDayCorrection.prototype.visitDate = function( componentDate ) {

    if( componentDate != null ) {

        var calendar = new SimpleCalendar( componentDate.getSelectedDate() );

        var selectedYear = Number( componentDate.getSelectedYear() );
        var selectedMonth = Number( componentDate.getSelectedMonth() );
        var selectedDay = Number( componentDate.getSelectedDay() );

        if( isNaN( selectedYear ) == false &&
            isNaN( selectedMonth ) == false &&
            isNaN( selectedDay )== false
        ) {


            var dayNode = componentDate.getDayComponent().getFormNode();
            var dayValues = dayNode.getPossibleValues();
            var maxDays = calendar.getDaysInMonth( selectedMonth , selectedYear );
            var lastDay = Number( dayValues[ dayValues.length -1 ] );

            if( lastDay < maxDays ) {
		//alert( "lastDay < maxDays ; " + lastDay + " < " + maxDays );
                var startIndex = lastDay + 1;
                var endIndex = maxDays;
                for( var index = startIndex; index <= endIndex; index++ ) {
                    dayNode.addPossibleValue( index, index );
                }
            }
            else
            if( lastDay > maxDays ) {
		//alert( "lastDay > maxDays ; " + lastDay + " > " + maxDays );
                var wasSelectedRemoved = false;
                var startIndex = lastDay;
                var endIndex = maxDays + 1 ;
                for( var index = startIndex; index >= endIndex; index-- ) {
                    dayNode.removePossibleValue( index );

                    if( index == selectedDay ) {
                        wasSelectedRemoved = true;
                    }
                }
                if( wasSelectedRemoved == true ) {
                    dayNode.setSelectedValue( maxDays );
                }
            }
        }
    }

};

VisitorDateRangeWatcher.prototype = new AbstractVisitor();
VisitorDateRangeWatcher.prototype.constructor = VisitorDateRangeWatcher;
VisitorDateRangeWatcher.superclass = AbstractVisitor.prototype;

function VisitorDateRangeWatcher() {
};

VisitorDateRangeWatcher.prototype.visitDateRange = function( dateRangeComponent ) {

    if( dateRangeComponent != null ) {

        //var stateMsg = "before: " + dateRange.getRangeAsString();

        var fromDate = dateRangeComponent.getFromDateComponent().getSelectedDate();
        var toDate = dateRangeComponent.getToDateComponent().getSelectedDate();

        var duration = 1;
        // fromdate > toDate, have been automatically switched in their daterange instance
        if( dateRangeComponent.hasDurationComponent() ) {
            // set fromDate + duration
            duration = Number( dateRangeComponent.getSelectedDuration() );
            if( isNaN( duration ) || duration < 1 ) {
                duration = 1;
            }
        } else {
            // calculate current duration, old date already lost
            //duration = new DateRange( fromDate, toDate ).getDurationDays();
            //alert( fromDate + " - " + toDate + " = " + duration );
        }

        if( fromDate.getTime() >= toDate.getTime() ) {

            var calendarToUse = new SimpleCalendar();

            if( toDate.getFullYear() == fromDate.getFullYear() && toDate.getMonth() == fromDate.getMonth()  ) {
                // day change occured, just add 1 month
                calendarToUse = new SimpleCalendar( new Date( toDate.getFullYear(), toDate.getMonth(), toDate.getDate() ) );
                //alert( "Adding 1 month...");
                calendarToUse.addMonths( 1 );
            } else {
                //proceed as always
                calendarToUse = new SimpleCalendar( new Date( fromDate.getFullYear(), fromDate.getMonth(), fromDate.getDate() ) );
                //alert( "Adding duration...");
                // set fromDate + duration
                calendarToUse.addDays( duration );
            }

            //alert( "Attempting to set date to " + calendarToUse.getTime() );
            dateRangeComponent.getToDateComponent().setSelectedDate( calendarToUse.getTime() );

        }
        else {

            var dateRange = dateRangeComponent.getDateRange();
            var diff = duration - ( dateRange.getDurationDays() );

            if( diff > 0 ) {
                var calendar = new SimpleCalendar( new Date( dateRange.toDate.getFullYear(), dateRange.toDate.getMonth(), dateRange.toDate.getDate() ) );
                calendar.addDays(  diff );
                dateRangeComponent.getToDateComponent().setSelectedDate( calendar.calendarDate );
            }
        }
    }
};

VisitorDateAndDuration.prototype = new AbstractVisitor();
VisitorDateAndDuration.prototype.constructor = VisitorDateAndDuration;
VisitorDateAndDuration.superclass = AbstractVisitor.prototype;

function VisitorDateAndDuration() {
};


VisitorDateAndDuration.prototype.visitDateRange = function( dateRangeComponent, modifyDurationOnly, durationMax ) {
    if( dateRangeComponent != null ) {
        if( dateRangeComponent.hasDurationComponent() ) {

            if( modifyDurationOnly ) {
                var dateRange = dateRangeComponent.getDateRange();
                if( dateRange != null ) {

                    var currentDuration = dateRangeComponent.getDuration();

                    var duration = dateRange.getDurationDays();
                    if( dateRange.isSwitchedDates() ) {
                        duration = duration*-1;
                    }
                    if( duration > 0 && duration != currentDuration  ) {
                        if( durationMax != null ) {
                            if( duration <= durationMax ) {
                                dateRangeComponent.setDuration( duration );
                            } else {
                                dateRangeComponent.setDuration( durationMax );
                            }
                        } else {
                            dateRangeComponent.setDuration( duration );
                        }
                    } /*else {
                        dateRangeComponent.setDuration( 0 );
                    }*/
                }

            } else {
                var duration = 1;
                duration = Number( dateRangeComponent.getSelectedDuration() - 1 );
                if( isNaN( duration ) || duration < 1 ) {
                    duration = 1;
                }

                var fromDate = dateRangeComponent.getFromDateComponent().getSelectedDate();
                var toDate = dateRangeComponent.getToDateComponent().getSelectedDate();

                // set todate = fromdate + duration
                var calendar = new SimpleCalendar( new Date( fromDate.getFullYear(), fromDate.getMonth(), fromDate.getDate() ) );
                calendar.addDays( duration );
                dateRangeComponent.getToDateComponent().setSelectedDate( calendar.calendarDate );
            }

        }

    }
};

VisitorHierachyTree.prototype = new AbstractVisitor();
VisitorHierachyTree.prototype.constructor = VisitorHierachyTree;
VisitorHierachyTree.superclass = AbstractVisitor.prototype;

function VisitorHierachyTree() {
};

VisitorHierachyTree.prototype.visitHierachy = function( componentHierachy ) {
    var disable = this.isEnabled( componentHierachy ) == false
    this.traverseTree( componentHierachy, disable );
    this.traverseTreeReverse( componentHierachy );
};


VisitorHierachyTree.prototype.switchState = function( componentHierachy ) {

    if( componentHierachy != null ) {

        var currentFormNode = componentHierachy.formNode;
        if( currentFormNode != null ) {

            var value = componentHierachy.formNode.getValue();

            if( this.isEnabled( componentHierachy ) ) {
                currentFormNode.removeSelectedValue( value );
            } else {
                currentFormNode.setSelectedValue( value );
            }
        }
    }

};

VisitorHierachyTree.prototype.adaptState = function( componentHierachy, disable ) {

    if( componentHierachy != null ) {
        var isEnabled = this.isEnabled( componentHierachy );

        if( isEnabled && disable ) {
            this.switchState( componentHierachy );
        }

        if( !isEnabled && !disable ) {
            this.switchState( componentHierachy );
        }
    }
};

VisitorHierachyTree.prototype.isEnabled = function( componentHierachy ) {
    var enabled = false;
    if( componentHierachy != null ) {

        var currentFormNode = componentHierachy.formNode;
        if( currentFormNode != null ) {

            var value = componentHierachy.formNode.getValue();
            enabled = currentFormNode.hasSelectedValue( value );
        }
    }
    return enabled;
};

VisitorHierachyTree.prototype.allChildsEnabled = function( componentHierachy ) {
    var allEnabled = false;
    if( componentHierachy != null ) {
        if( componentHierachy.isLeaf() == false ) {

            allEnabled = true;

            var childs = componentHierachy.getComponents();

            for( var childIndex = 0; childIndex < childs.length && allEnabled == true ; childIndex++ ) {
                var nextChild = childs[ childIndex ];
                // for some unknown reason root is in its components
                if( nextChild.isRoot() == false && nextChild.getFormNode().getValue() != componentHierachy.getFormNode().getValue() ) {
                    if( this.isEnabled( nextChild ) ) {
                        allEnabled = this.allChildsEnabled( nextChild );
                    }
                    else {
                        allEnabled = false;
                    }
                }
            }

        } else {
            allEnabled = true;
        }
    }
    return allEnabled;
};

VisitorHierachyTree.prototype.traverseTreeReverse = function( componentHierachy ) {

    if( componentHierachy != null ) {

        if( !componentHierachy.isLeaf() ) {

            if( this.allChildsEnabled( componentHierachy ) ) {
                this.adaptState( componentHierachy , false );
            } else {
                this.adaptState( componentHierachy , true );
            }
        }

        if( !componentHierachy.isRoot() ) {
            this.traverseTreeReverse( componentHierachy.getParent() );
        }
    }
};



VisitorHierachyTree.prototype.traverseTree = function( componentHierachy, disable ) {
    if( componentHierachy != null ) {

        this.adaptState( componentHierachy, disable );

        var childs = componentHierachy.getComponents();
        for( var childIndex = 0; childs != null && childIndex < childs.length; childIndex++ ) {
            this.traverseTree( childs[ childIndex ], disable );
        }
    }
};


ComponentGroupExtender.prototype = new AbstractVisitor();
ComponentGroupExtender.prototype.constructor = ComponentGroupExtender;
ComponentGroupExtender.superclass = AbstractVisitor.prototype;

function ComponentGroupExtender() {
};


ComponentGroupExtender.prototype.visitFormNodeGroup = function( componentFormNodeGroup ) {

    if( componentFormNodeGroup != null ) {
        var groupElements = componentFormNodeGroup.getComponents();
        if( groupElements != null && groupElements.length > 0 ) {

            if( this.hasEnabled( componentFormNodeGroup ) ) {

                if( this.allEnabled( componentFormNodeGroup ) == false ) {
                    // enabled missing elements

                    for( var index = 0; index < groupElements.length ; index++ ) {

                         if( this.isEnabled( groupElements[ index ] ) == false ) {
                            this.enableComponent( groupElements[ index ] );
                         }
                    }

                }
            }
        }
    }
};

ComponentGroupExtender.prototype.getHiddenElement = function( componentFormNode ) {
    var hiddenFormNode = null;

    if( componentFormNode ) {
        var formNode = componentFormNode.getFormNode().formElementNode.form;
        var value = componentFormNode.formNode.getValue();
        if( formNode != null ) {

            var groupNodes = getNodesWithNameAndAttributeValue( formNode, FormNodeMetadata.ELEMENT_INPUT, "value", value );
            if( groupNodes != null && groupNodes.length > 1 ) {

                for( var index = 0; index < groupNodes.length && hiddenFormNode == null ; index++ ) {
                    var nextNode = new FormNode( groupNodes[ index ] );
                    if( nextNode.metaData.isHidden() ) {
                        hiddenFormNode = nextNode;
                    }
                }
            }
        }
    }

    return hiddenFormNode;
};


ComponentGroupExtender.prototype.createHiddenElement = function( componentFormNode ) {

    var hiddenFormNode = this.getHiddenElement( componentFormNode );
    if( hiddenFormNode == null && componentFormNode != null ) {

        var formNode = componentFormNode.getFormNode();
        var hiddenNode = formNode.formElementNode.ownerDocument.createElement( "input" );

        var hiddenAttr = formNode.formElementNode.ownerDocument.createAttribute( "type");
        hiddenAttr.value = "hidden";

        var nameAttr = formNode.formElementNode.ownerDocument.createAttribute( "name");
        nameAttr.value = formNode.getName();

        var valueAttr = formNode.formElementNode.ownerDocument.createAttribute( "value");
        valueAttr.value = formNode.getValue();

        hiddenNode.setAttributeNode( hiddenAttr );
        hiddenNode.setAttributeNode( nameAttr );
        hiddenNode.setAttributeNode( valueAttr );

        formNode.formElementNode.form.appendChild( hiddenNode );
    }
};

ComponentGroupExtender.prototype.removeHiddenElement = function( componentFormNode ) {
    var hiddenFormNode = this.getHiddenElement( componentFormNode );
    if( hiddenFormNode != null ) {
        hiddenFormNode.componentFormNode.getFormNode().formElementNode.form.removeChild( hiddenFormNode );
    }
};


ComponentGroupExtender.prototype.allEnabled = function( componentFormNodeGroup ) {
    var enabled = false;

    var groupElements = componentFormNodeGroup.getComponents();
    if( groupElements != null && groupElements.length > 0 ) {

        enabled = true;
        for( var index = 0; index < groupElements.length && enabled == true ; index++ ) {
            enabled = this.isEnabled( groupElements[index ] );
        }
    }

    return enabled;

};

ComponentGroupExtender.prototype.hasEnabled = function( componentFormNodeGroup ) {
    var enabled = false;

    var groupElements = componentFormNodeGroup.getComponents();
    if( groupElements != null && groupElements.length > 0 ) {

        for( var index = 0; index < groupElements.length && enabled == false ; index++ ) {
            enabled = this.isEnabled( groupElements[index ] );
        }
    }

    return enabled;
};

ComponentGroupExtender.prototype.isEnabled = function( componentFormNode ) {
    var enabled = false;
    if( componentFormNode != null ) {

        var currentFormNode = componentFormNode.formNode;
        if( currentFormNode != null ) {

            var value = currentFormNode.getValue();
            enabled = currentFormNode.hasSelectedValue( value );
        }
    }
    return enabled;
};

ComponentGroupExtender.prototype.enableComponent = function( componentFormNode ) {
    if( componentFormNode != null ) {
        var currentFormNode = componentFormNode.formNode;
        if( currentFormNode != null ) {
            currentFormNode.setSelectedValue( currentFormNode.getValue() );
        }
    }
}
