001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (C) Alkacon Software (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.acacia.client;
029
030import org.opencms.acacia.client.CmsUndoRedoHandler.ChangeType;
031import org.opencms.acacia.client.css.I_CmsLayoutBundle;
032import org.opencms.acacia.client.entity.I_CmsEntityBackend;
033import org.opencms.acacia.client.ui.CmsAttributeValueView;
034import org.opencms.acacia.client.ui.CmsInlineEntityWidget;
035import org.opencms.acacia.client.widgets.I_CmsFormEditWidget;
036import org.opencms.acacia.shared.CmsEntity;
037import org.opencms.acacia.shared.CmsEntityAttribute;
038import org.opencms.acacia.shared.CmsType;
039import org.opencms.gwt.client.dnd.CmsDNDHandler;
040import org.opencms.gwt.client.dnd.CmsDNDHandler.Orientation;
041import org.opencms.gwt.client.ui.CmsTabbedPanel;
042import org.opencms.gwt.client.util.CmsMoveAnimation;
043
044import java.util.ArrayList;
045import java.util.List;
046
047import com.google.gwt.dom.client.Element;
048import com.google.gwt.dom.client.Style.Position;
049import com.google.gwt.dom.client.Style.Unit;
050import com.google.gwt.event.logical.shared.ResizeHandler;
051import com.google.gwt.user.client.Command;
052import com.google.gwt.user.client.ui.FlowPanel;
053import com.google.gwt.user.client.ui.Panel;
054import com.google.gwt.user.client.ui.Widget;
055
056/**
057 * The attribute handler. Handles value changes, addition of new values, remove and move operations on values.<p>
058 */
059public class CmsAttributeHandler extends CmsRootHandler {
060
061    /** The global widget resize handler. */
062    private static ResizeHandler m_resizeHandler;
063
064    /** The scroll element. */
065    private static Element m_scrollElement;
066
067    /** The attribute name. */
068    private String m_attributeName;
069
070    /** The attribute type. */
071    private CmsType m_attributeType;
072
073    /** Registered attribute values. */
074    private List<CmsAttributeValueView> m_attributeValueViews;
075
076    /** The attribute drag and drop handler. */
077    private CmsDNDHandler m_dndHandler;
078
079    /** The entity. */
080    private CmsEntity m_entity;
081
082    /** The entity back end instance. */
083    private I_CmsEntityBackend m_entityBackEnd;
084
085    /** The entity type. */
086    private CmsType m_entityType;
087
088    /** The parent attribute handler. */
089    private I_CmsAttributeHandler m_parentHandler;
090
091    /** The single value index. */
092    private int m_singleValueIndex;
093
094    /** The widget service. */
095    private I_CmsWidgetService m_widgetService;
096
097    /**
098     * Constructor.<p>
099     *
100     * @param entityBackEnd the entity back end instance
101     * @param entity the entity
102     * @param attributeName the attribute name
103     * @param widgetService the widget service
104     */
105    public CmsAttributeHandler(
106        I_CmsEntityBackend entityBackEnd,
107        CmsEntity entity,
108        String attributeName,
109        I_CmsWidgetService widgetService) {
110
111        // single value handling is disable by default
112        m_singleValueIndex = -1;
113        m_entityBackEnd = entityBackEnd;
114        m_entity = entity;
115        m_attributeName = attributeName;
116        m_widgetService = widgetService;
117        m_attributeValueViews = new ArrayList<CmsAttributeValueView>();
118        if (!getAttributeType().isSimpleType()) {
119            int count = 0;
120            CmsEntityAttribute attribute = entity.getAttribute(attributeName);
121            if (attribute != null) {
122                count = attribute.getValueCount();
123            }
124            initHandlers(count);
125        }
126    }
127
128    /**
129     * Clears the error styles on the given tabbed panel.<p>
130     *
131     * @param tabbedPanel the tabbed panel
132     */
133    public static void clearErrorStyles(CmsTabbedPanel<?> tabbedPanel) {
134
135        for (int i = 0; i < tabbedPanel.getTabCount(); i++) {
136            Widget tab = tabbedPanel.getTabWidget(i);
137            tab.setTitle(null);
138            tab.getParent().removeStyleName(I_CmsLayoutBundle.INSTANCE.form().hasError());
139            tab.getParent().removeStyleName(I_CmsLayoutBundle.INSTANCE.form().hasWarning());
140        }
141    }
142
143    /**
144     * Returns the global widget resize handler.<p>
145     *
146     * @return the global widget resize handler
147     */
148    public static ResizeHandler getResizeHandler() {
149
150        return m_resizeHandler;
151    }
152
153    /**
154     * Returns <code>true</code> if a global widget resize handler is present.<p>
155     *
156     * @return <code>true</code> if a global widget resize handler is present
157     */
158    public static boolean hasResizeHandler() {
159
160        return m_resizeHandler != null;
161    }
162
163    /**
164     * Sets the global widget resize handler.<p>
165     *
166     * @param handler the resize handler
167     */
168    public static void setResizeHandler(ResizeHandler handler) {
169
170        m_resizeHandler = handler;
171    }
172
173    /**
174     * Sets the scroll element. To be used for automatic scrolling during drag and drop.<p>
175     *
176     * @param scrollElement the scroll element
177     */
178    public static void setScrollElement(Element scrollElement) {
179
180        m_scrollElement = scrollElement;
181    }
182
183    /**
184     * Adds a new attribute value below the reference view.<p>
185     *
186     * @param reference the reference value view
187     */
188    public void addNewAttributeValue(CmsAttributeValueView reference) {
189
190        // make sure not to add more values than allowed
191        int maxOccurrence = getEntityType().getAttributeMaxOccurrence(m_attributeName);
192        CmsEntityAttribute attribute = m_entity.getAttribute(m_attributeName);
193        boolean mayHaveMore = ((attribute == null) || (attribute.getValueCount() < maxOccurrence));
194        if (mayHaveMore) {
195            if (getAttributeType().isSimpleType()) {
196                int valueIndex = reference.getValueIndex() + 1;
197                String defaultValue = m_widgetService.getDefaultAttributeValue(
198                    m_attributeName,
199                    getSimplePath(valueIndex));
200                I_CmsFormEditWidget widget = m_widgetService.getAttributeFormWidget(m_attributeName);
201
202                boolean insertLast = false;
203                if (reference.getElement().getNextSiblingElement() == null) {
204                    m_entity.addAttributeValue(m_attributeName, defaultValue);
205                    insertLast = true;
206                } else {
207                    valueIndex = reference.getValueIndex() + 1;
208                    m_entity.insertAttributeValue(m_attributeName, defaultValue, valueIndex);
209                    m_widgetService.addChangedOrderPath(getSimplePath(-1));
210                }
211                CmsAttributeValueView valueWidget = reference;
212                if (reference.hasValue()) {
213                    valueWidget = new CmsAttributeValueView(
214                        this,
215                        m_widgetService.getAttributeLabel(m_attributeName),
216                        m_widgetService.getAttributeHelp(m_attributeName));
217                    if (m_widgetService.isDisplaySingleLine(m_attributeName)) {
218                        valueWidget.setCompactMode(CmsAttributeValueView.COMPACT_MODE_SINGLE_LINE);
219                    }
220                    if (insertLast) {
221                        ((FlowPanel)reference.getParent()).add(valueWidget);
222                    } else {
223                        ((FlowPanel)reference.getParent()).insert(valueWidget, valueIndex);
224                    }
225
226                }
227                valueWidget.setValueWidget(widget, defaultValue, defaultValue, true);
228            } else {
229                CmsEntity value = m_entityBackEnd.createEntity(null, getAttributeType().getId());
230                insertValueAfterReference(value, reference);
231            }
232            CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
233            if (handler.isIntitalized()) {
234                handler.addChange(m_entity.getId(), m_attributeName, reference.getValueIndex() + 1, ChangeType.add);
235            }
236        }
237        updateButtonVisisbility();
238    }
239
240    /**
241     * Adds a new attribute value and adds the required widgets to the editor DOM.<p>
242     *
243     * @param value the value entity
244     */
245    public void addNewAttributeValue(CmsEntity value) {
246
247        // make sure not to add more values than allowed
248        int maxOccurrence = getEntityType().getAttributeMaxOccurrence(m_attributeName);
249        CmsEntityAttribute attribute = m_entity.getAttribute(m_attributeName);
250        boolean mayHaveMore = ((attribute == null) || (attribute.getValueCount() < maxOccurrence));
251        if (mayHaveMore && value.getTypeName().equals(m_attributeType.getId())) {
252            m_entity.addAttributeValue(m_attributeName, value);
253            int valueIndex = m_entity.getAttribute(m_attributeName).getValueCount() - 1;
254            CmsAttributeValueView valueView = null;
255            if ((m_attributeValueViews.size() == 1) && !m_attributeValueViews.get(0).hasValue()) {
256                valueView = m_attributeValueViews.get(0);
257            } else {
258                valueView = new CmsAttributeValueView(
259                    this,
260                    m_widgetService.getAttributeLabel(m_attributeName),
261                    m_widgetService.getAttributeHelp(m_attributeName));
262            }
263
264            CmsRenderer.setAttributeChoice(m_widgetService, valueView, getAttributeType());
265            ((FlowPanel)m_attributeValueViews.get(0).getParent()).add(valueView);
266
267            insertHandlers(valueIndex);
268            I_CmsEntityRenderer renderer = m_widgetService.getRendererForAttribute(m_attributeName, getAttributeType());
269            valueView.setValueEntity(renderer, value);
270
271            CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
272            if (handler.isIntitalized()) {
273                handler.addChange(m_entity.getId(), m_attributeName, valueIndex, ChangeType.add);
274            }
275        }
276    }
277
278    /**
279     * Adds a new attribute value and adds the required widgets to the editor DOM.<p>
280     *
281     * @param value the simple value
282     */
283    public void addNewAttributeValue(String value) {
284
285        // make sure not to add more values than allowed
286        int maxOccurrence = getEntityType().getAttributeMaxOccurrence(m_attributeName);
287        CmsEntityAttribute attribute = m_entity.getAttribute(m_attributeName);
288        boolean mayHaveMore = ((attribute == null) || (attribute.getValueCount() < maxOccurrence));
289        if (mayHaveMore && getAttributeType().isSimpleType()) {
290            I_CmsFormEditWidget widget = m_widgetService.getAttributeFormWidget(m_attributeName);
291            m_entity.addAttributeValue(m_attributeName, value);
292            int valueCount = m_entity.getAttribute(m_attributeName).getValueCount();
293            String defaultValue = m_widgetService.getDefaultAttributeValue(
294                m_attributeName,
295                getSimplePath(valueCount - 1));
296            CmsAttributeValueView valueView = null;
297            if ((m_attributeValueViews.size() == 1) && !m_attributeValueViews.get(0).hasValue()) {
298                valueView = m_attributeValueViews.get(0);
299                valueView.setActive();
300                // setActive may have reset the value, so we set it again
301                m_entity.setAttributeValue(m_attributeName, value, valueCount - 1);
302                valueView.getValueWidget().setValue(value);
303            } else {
304                valueView = new CmsAttributeValueView(
305                    this,
306                    m_widgetService.getAttributeLabel(m_attributeName),
307                    m_widgetService.getAttributeHelp(m_attributeName));
308                if (m_widgetService.isDisplaySingleLine(m_attributeName)) {
309                    valueView.setCompactMode(CmsAttributeValueView.COMPACT_MODE_SINGLE_LINE);
310                }
311                ((FlowPanel)m_attributeValueViews.get(0).getParent()).add(valueView);
312                valueView.setValueWidget(widget, value, defaultValue, true);
313            }
314            CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
315            if (handler.isIntitalized()) {
316                handler.addChange(
317                    m_entity.getId(),
318                    m_attributeName,
319                    m_entity.getAttribute(m_attributeName).getValueCount() - 1,
320                    ChangeType.add);
321            }
322            updateButtonVisisbility();
323        }
324    }
325
326    /**
327     * Adds a new attribute value below the reference index.<p>
328     * This will not execute any DOM manipulations.<p>
329     *
330     * @param referenceIndex the reference value index
331     */
332    public void addNewAttributeValueToEntity(int referenceIndex) {
333
334        // make sure not to add more values than allowed
335        int maxOccurrence = getEntityType().getAttributeMaxOccurrence(m_attributeName);
336        CmsEntityAttribute attribute = m_entity.getAttribute(m_attributeName);
337        boolean mayHaveMore = ((attribute == null) || (attribute.getValueCount() < maxOccurrence));
338        if (mayHaveMore) {
339            if (getAttributeType().isSimpleType()) {
340                String defaultValue = m_widgetService.getDefaultAttributeValue(
341                    m_attributeName,
342                    getSimplePath(referenceIndex + 1));
343                if ((attribute == null) || (attribute.getValueCount() == (referenceIndex + 1))) {
344                    m_entity.addAttributeValue(m_attributeName, defaultValue);
345                } else {
346                    m_entity.insertAttributeValue(m_attributeName, defaultValue, referenceIndex + 1);
347                    m_widgetService.addChangedOrderPath(getSimplePath(-1));
348                }
349            } else {
350                CmsEntity value = m_entityBackEnd.createEntity(null, m_attributeType.getId());
351                if ((attribute == null) || (attribute.getValueCount() == (referenceIndex + 1))) {
352                    m_entity.addAttributeValue(m_attributeName, value);
353                } else {
354                    m_entity.insertAttributeValue(m_attributeName, value, referenceIndex + 1);
355                    m_widgetService.addChangedOrderPath(getSimplePath(-1));
356                }
357                insertHandlers(referenceIndex + 1);
358            }
359        }
360    }
361
362    /**
363     * Adds a new choice attribute value.<p>
364     *
365     * @param reference the reference value view
366     * @param choicePath the path of the selected (possibly nested) choice attribute, consisting of attribute names
367     */
368    public void addNewChoiceAttributeValue(CmsAttributeValueView reference, List<String> choicePath) {
369
370        CmsValueFocusHandler.getInstance().clearFocus();
371        m_widgetService.addChangedOrderPath(getSimplePath(-1));
372        if (isChoiceHandler()) {
373            addChoiceOption(reference, choicePath);
374        } else {
375            addComplexChoiceValue(reference, choicePath);
376        }
377        updateButtonVisisbility();
378        CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
379        if (handler.isIntitalized()) {
380            handler.addChange(m_entity.getId(), m_attributeName, reference.getValueIndex() + 1, ChangeType.choice);
381        }
382    }
383
384    /**
385     * Applies a value change to the entity data as well as to the value view widget.<p>
386     *
387     * @param value the value
388     * @param valueIndex the value index
389     */
390    public void changeValue(String value, int valueIndex) {
391
392        m_attributeValueViews.get(valueIndex).getValueWidget().setValue(value, false);
393        changeEntityValue(value, valueIndex);
394    }
395
396    /**
397     * @see org.opencms.acacia.client.CmsRootHandler#collectSimplePath(org.opencms.acacia.client.I_CmsAttributeHandler)
398     */
399    @Override
400    public String collectSimplePath(I_CmsAttributeHandler childHandler) {
401
402        int index = -1;
403        for (int i = 0; i < m_handlers.size(); i++) {
404            if (m_handlers.get(i).get(childHandler.getAttributeName()) == childHandler) {
405                index = i;
406                break;
407            }
408        }
409        if (index == -1) {
410            throw new RuntimeException("Child handler is not properly registered.");
411        }
412        return getSimplePath(index) + "/";
413    }
414
415    /**
416     * Creates a sequence of nested entities according to a given path of choice attribute names.<p>
417     *
418     * @param value the entity into which the new entities for the given path should be inserted
419     * @param choicePath the path of choice attributes
420     */
421    public void createNestedEntitiesForChoicePath(CmsEntity value, List<String> choicePath) {
422
423        CmsEntity parentValue = value;
424        for (String attributeChoice : choicePath) {
425            CmsType choiceType = m_entityBackEnd.getType(parentValue.getTypeName()).getAttributeType(
426                CmsType.CHOICE_ATTRIBUTE_NAME);
427            CmsEntity choice = m_entityBackEnd.createEntity(null, choiceType.getId());
428            parentValue.addAttributeValue(CmsType.CHOICE_ATTRIBUTE_NAME, choice);
429            CmsType choiceOptionType = choiceType.getAttributeType(attributeChoice);
430            if (choiceOptionType.isSimpleType()) {
431                String choiceValue = m_widgetService.getDefaultAttributeValue(attributeChoice, getSimplePath(0));
432                choice.addAttributeValue(attributeChoice, choiceValue);
433                break;
434            } else {
435                CmsEntity choiceValue = m_entityBackEnd.createEntity(null, choiceOptionType.getId());
436                choice.addAttributeValue(attributeChoice, choiceValue);
437                parentValue = choiceValue;
438            }
439        }
440    }
441
442    /**
443     * Destroys the attribute handler instance.<p>
444     */
445    public void destroy() {
446
447        m_attributeName = null;
448        m_attributeType = null;
449        m_attributeValueViews.clear();
450        m_attributeValueViews = null;
451        m_dndHandler = null;
452        m_entity = null;
453        m_entityType = null;
454        m_entityBackEnd = null;
455        m_widgetService = null;
456    }
457
458    /**
459     * Returns the attribute name.<p>
460     *
461     * @return the attribute name
462     */
463    @Override
464    public String getAttributeName() {
465
466        return m_attributeName;
467    }
468
469    /**
470     * Returns the attribute type.<p>
471     *
472     * @return the attribute type
473     */
474    public CmsType getAttributeType() {
475
476        if (m_attributeType == null) {
477            m_attributeType = getEntityType().getAttributeType(m_attributeName);
478        }
479        return m_attributeType;
480    }
481
482    /**
483     * Returns the drag and drop handler.<p>
484     *
485     * @return the drag and drop handler
486     */
487    public CmsDNDHandler getDNDHandler() {
488
489        if (m_dndHandler == null) {
490            m_dndHandler = new CmsDNDHandler(new CmsAttributeDNDController());
491            m_dndHandler.setOrientation(Orientation.VERTICAL);
492            m_dndHandler.setScrollEnabled(true);
493            m_dndHandler.setScrollElement(m_scrollElement);
494        }
495        return m_dndHandler;
496    }
497
498    /**
499     * Returns the entity id.<p>
500     *
501     * @return the entity id
502     */
503    public String getEntityId() {
504
505        return m_entity.getId();
506    }
507
508    /**
509     * Gets the maximum occurrence of the attribute.<p>
510     *
511     * @return the maximum occurrence
512     */
513    public int getMaxOccurence() {
514
515        return getEntityType().getAttributeMaxOccurrence(m_attributeName);
516    }
517
518    /**
519     * Returns the simple value path for the given index.<p>
520     * This will use the last fragment of the attribute name and concatenate it with the parent path.<p>
521     * If the given index equals -1 no value index will be appended
522     *
523     * @param index the value index
524     *
525     * @return the simple path
526     */
527    public String getSimplePath(int index) {
528
529        String simpleName = m_attributeName.substring(m_attributeName.lastIndexOf("/") + 1);
530        String result = m_parentHandler.collectSimplePath(this) + simpleName;
531        if (index != -1) {
532            result += "[" + (index + 1) + "]";
533        }
534        return result;
535    }
536
537    /**
538     * Gets the widget service.<p>
539     *
540     * @return the widget service
541     */
542    public I_CmsWidgetService getWidgetService() {
543
544        return m_widgetService;
545    }
546
547    /**
548     * Handles value changes from the view.<p>
549     *
550     * @param reference the attribute value reference
551     * @param value the value
552     */
553    public void handleValueChange(CmsAttributeValueView reference, String value) {
554
555        handleValueChange(reference.getValueIndex(), value);
556    }
557
558    /**
559     * Handles value changes from the view.<p>
560     *
561     * @param valueIndex the value index
562     * @param value the value
563     */
564    public void handleValueChange(int valueIndex, String value) {
565
566        if (isSingleValueHandler()) {
567            valueIndex = m_singleValueIndex;
568        }
569        changeEntityValue(value, valueIndex);
570        CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
571        if (handler.isIntitalized()) {
572            handler.addChange(m_entity.getId(), m_attributeName, valueIndex, ChangeType.value);
573        }
574    }
575
576    /**
577     * Return true if there is a single remaining value, which is optional.<p>
578     *
579     * @return true if this has only one optional value
580     */
581    public boolean hasSingleOptionalValue() {
582
583        return ((getEntityType().getAttributeMinOccurrence(m_attributeName) == 0)
584            && (m_entity.getAttribute(m_attributeName) != null)
585            && (m_entity.getAttribute(m_attributeName).getValueCount() == 1));
586    }
587
588    /**
589     * Returns if there is a value view widget registered for the given index.<p>
590     *
591     * @param valueIndex the value index
592     *
593     * @return <code>true</code> if there is a value view widget registered for the given index
594     */
595    public boolean hasValueView(int valueIndex) {
596
597        return m_attributeValueViews.size() > valueIndex;
598    }
599
600    /**
601     * Adds a new attribute value and adds the required widgets to the editor DOM.<p>
602     *
603     * @param value the value entity
604     * @param index the position in which to insert the new value
605     * @param container the widget containing the attribute value views
606     */
607    public void insertNewAttributeValue(CmsEntity value, int index, Panel container) {
608
609        // make sure not to add more values than allowed
610        int maxOccurrence = getEntityType().getAttributeMaxOccurrence(m_attributeName);
611        CmsEntityAttribute attribute = m_entity.getAttribute(m_attributeName);
612        boolean mayHaveMore = ((attribute == null) || (attribute.getValueCount() < maxOccurrence));
613        if (mayHaveMore && value.getTypeName().equals(m_attributeType.getId())) {
614            m_entity.insertAttributeValue(m_attributeName, value, index);
615            int valueIndex = index;
616            CmsAttributeValueView valueView = null;
617            if ((m_attributeValueViews.size() == 1) && !m_attributeValueViews.get(0).hasValue()) {
618                valueView = m_attributeValueViews.get(0);
619            } else {
620                valueView = new CmsAttributeValueView(
621                    this,
622                    m_widgetService.getAttributeLabel(m_attributeName),
623                    m_widgetService.getAttributeHelp(m_attributeName));
624            }
625            CmsRenderer.setAttributeChoice(m_widgetService, valueView, getAttributeType());
626            m_attributeValueViews.remove(valueView);
627            m_attributeValueViews.add(index, valueView);
628
629            ((FlowPanel)container).insert(valueView, index);
630
631            insertHandlers(valueIndex);
632
633            I_CmsEntityRenderer renderer = m_widgetService.getRendererForAttribute(m_attributeName, getAttributeType());
634            valueView.setValueEntity(renderer, value);
635
636            CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
637            if (handler.isIntitalized()) {
638                handler.addChange(m_entity.getId(), m_attributeName, valueIndex, ChangeType.add);
639            }
640        }
641    }
642
643    /**
644     * Returns if this is a choice handler.<p>
645     *
646     * @return <code>true</code> if this is a choice handler
647     */
648    public boolean isChoiceHandler() {
649
650        return CmsType.CHOICE_ATTRIBUTE_NAME.equals(m_attributeName);
651    }
652
653    /**
654     * Moves the give attribute value from one position to another.<p>
655     *
656     * @param valueView the value to move
657     * @param currentPosition the current position
658     * @param targetPosition the target position
659     */
660    public void moveAttributeValue(CmsAttributeValueView valueView, int currentPosition, int targetPosition) {
661
662        if (currentPosition == targetPosition) {
663            return;
664        }
665        FlowPanel parent = (FlowPanel)valueView.getParent();
666        m_widgetService.addChangedOrderPath(getSimplePath(-1));
667        valueView.removeFromParent();
668        m_attributeValueViews.remove(valueView);
669        CmsAttributeValueView valueWidget = null;
670        if (isChoiceHandler()) {
671            removeHandlers(currentPosition);
672            CmsEntity value = m_entity.getAttribute(m_attributeName).getComplexValues().get(currentPosition);
673            m_entity.removeAttributeValue(m_attributeName, currentPosition);
674            m_entity.insertAttributeValue(m_attributeName, value, targetPosition);
675            String attributeChoice = getChoiceName(targetPosition);
676            CmsType optionType = getAttributeType().getAttributeType(attributeChoice);
677            valueWidget = new CmsAttributeValueView(
678                this,
679                m_widgetService.getAttributeLabel(attributeChoice),
680                m_widgetService.getAttributeHelp(attributeChoice));
681            if (optionType.isSimpleType() && m_widgetService.isDisplaySingleLine(attributeChoice)) {
682                valueWidget.setCompactMode(CmsAttributeValueView.COMPACT_MODE_SINGLE_LINE);
683            }
684            parent.insert(valueWidget, targetPosition);
685            insertHandlers(targetPosition);
686            if (optionType.isSimpleType()) {
687                valueWidget.setValueWidget(
688                    m_widgetService.getAttributeFormWidget(attributeChoice),
689                    value.getAttribute(attributeChoice).getSimpleValue(),
690                    m_widgetService.getDefaultAttributeValue(attributeChoice, getSimplePath(targetPosition)),
691                    true);
692            } else {
693                valueWidget.setValueEntity(
694                    m_widgetService.getRendererForAttribute(attributeChoice, getAttributeType()),
695                    value.getAttribute(attributeChoice).getComplexValue());
696            }
697
698            List<CmsChoiceMenuEntryBean> menuEntries = CmsRenderer.getChoiceEntries(getAttributeType(), true);
699            for (CmsChoiceMenuEntryBean menuEntry : menuEntries) {
700                valueWidget.addChoice(m_widgetService, menuEntry);
701            }
702        } else if (getAttributeType().isSimpleType()) {
703            String value = m_entity.getAttribute(m_attributeName).getSimpleValues().get(currentPosition);
704            m_entity.removeAttributeValue(m_attributeName, currentPosition);
705            m_entity.insertAttributeValue(m_attributeName, value, targetPosition);
706            valueWidget = new CmsAttributeValueView(
707                this,
708                m_widgetService.getAttributeLabel(m_attributeName),
709                m_widgetService.getAttributeHelp(m_attributeName));
710            if (m_widgetService.isDisplaySingleLine(m_attributeName)) {
711                valueWidget.setCompactMode(CmsAttributeValueView.COMPACT_MODE_SINGLE_LINE);
712            }
713            parent.insert(valueWidget, targetPosition);
714            valueWidget.setValueWidget(
715                m_widgetService.getAttributeFormWidget(m_attributeName),
716                value,
717                m_widgetService.getDefaultAttributeValue(m_attributeName, getSimplePath(targetPosition)),
718                true);
719        } else {
720            removeHandlers(currentPosition);
721            CmsEntity value = m_entity.getAttribute(m_attributeName).getComplexValues().get(currentPosition);
722            m_entity.removeAttributeValue(m_attributeName, currentPosition);
723            m_entity.insertAttributeValue(m_attributeName, value, targetPosition);
724            valueWidget = new CmsAttributeValueView(
725                this,
726                m_widgetService.getAttributeLabel(m_attributeName),
727                m_widgetService.getAttributeHelp(m_attributeName));
728            parent.insert(valueWidget, targetPosition);
729            insertHandlers(targetPosition);
730            valueWidget.setValueEntity(
731                m_widgetService.getRendererForAttribute(m_attributeName, getAttributeType()),
732                value);
733            if (getAttributeType().getAttributeType(CmsType.CHOICE_ATTRIBUTE_NAME) != null) {
734                List<CmsChoiceMenuEntryBean> menuEntries = CmsRenderer.getChoiceEntries(getAttributeType(), false);
735                for (CmsChoiceMenuEntryBean entry : menuEntries) {
736                    valueWidget.addChoice(m_widgetService, entry);
737                }
738            }
739        }
740        m_attributeValueViews.remove(valueWidget);
741        m_attributeValueViews.add(targetPosition, valueWidget);
742        updateButtonVisisbility();
743        CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
744        if (handler.isIntitalized()) {
745            handler.addChange(m_entity.getId(), m_attributeName, 0, ChangeType.sort);
746        }
747    }
748
749    /**
750     * Moves the reference value down in the value list.<p>
751     *
752     * @param reference the reference value
753     */
754    public void moveAttributeValueDown(final CmsAttributeValueView reference) {
755
756        final int index = reference.getValueIndex();
757        if (index >= (m_entity.getAttribute(m_attributeName).getValueCount() - 1)) {
758            return;
759        }
760        m_widgetService.addChangedOrderPath(getSimplePath(-1));
761        reference.hideAllButtons();
762        Element parent = reference.getElement().getParentElement();
763        parent.getStyle().setPosition(Position.RELATIVE);
764        final Element placeHolder = reference.getPlaceholder(null);
765        int top = reference.getElement().getOffsetTop();
766        int left = reference.getElement().getOffsetLeft();
767        int width = reference.getOffsetWidth();
768        reference.getElement().getStyle().setPosition(Position.ABSOLUTE);
769        reference.getElement().getStyle().setZIndex(5);
770        parent.insertAfter(placeHolder, reference.getElement().getNextSibling());
771        reference.getElement().getStyle().setTop(top, Unit.PX);
772        reference.getElement().getStyle().setLeft(left, Unit.PX);
773        reference.getElement().getStyle().setWidth(width, Unit.PX);
774        new CmsMoveAnimation(reference.getElement(), top, left, placeHolder.getOffsetTop(), left, new Command() {
775
776            public void execute() {
777
778                clearMoveAnimationStyles(placeHolder, reference);
779                moveAttributeValue(reference, index, index + 1);
780
781            }
782        }).run(200);
783        CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
784        if (handler.isIntitalized()) {
785            handler.addChange(m_entity.getId(), m_attributeName, 0, ChangeType.sort);
786        }
787    }
788
789    /**
790     * Moves the reference value up in the value list.<p>
791     *
792     * @param reference the reference value
793     */
794    public void moveAttributeValueUp(final CmsAttributeValueView reference) {
795
796        final int index = reference.getValueIndex();
797        if (index == 0) {
798            return;
799        }
800        m_widgetService.addChangedOrderPath(getSimplePath(-1));
801        reference.hideAllButtons();
802        Element parent = reference.getElement().getParentElement();
803        parent.getStyle().setPosition(Position.RELATIVE);
804        final Element placeHolder = reference.getPlaceholder(null);
805        int top = reference.getElement().getOffsetTop();
806        int left = reference.getElement().getOffsetLeft();
807        int width = reference.getOffsetWidth();
808        reference.getElement().getStyle().setPosition(Position.ABSOLUTE);
809        reference.getElement().getStyle().setZIndex(5);
810        parent.insertBefore(placeHolder, reference.getElement().getPreviousSibling());
811        reference.getElement().getStyle().setTop(top, Unit.PX);
812        reference.getElement().getStyle().setLeft(left, Unit.PX);
813        reference.getElement().getStyle().setWidth(width, Unit.PX);
814        new CmsMoveAnimation(reference.getElement(), top, left, placeHolder.getOffsetTop(), left, new Command() {
815
816            public void execute() {
817
818                clearMoveAnimationStyles(placeHolder, reference);
819                moveAttributeValue(reference, index, index - 1);
820
821            }
822        }).run(200);
823        CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
824        if (handler.isIntitalized()) {
825            handler.addChange(m_entity.getId(), m_attributeName, 0, ChangeType.sort);
826        }
827    }
828
829    /**
830     * Registers an attribute value view.<p>
831     *
832     * @param attributeValue the attribute value view
833     */
834    public void registerAttributeValue(CmsAttributeValueView attributeValue) {
835
836        m_attributeValueViews.add(attributeValue);
837    }
838
839    /**
840     * Removes the reference attribute value view.<p>
841     *
842     * @param reference the reference view
843     */
844    public void removeAttributeValue(CmsAttributeValueView reference) {
845
846        removeAttributeValue(reference, false);
847    }
848
849    /**
850     * Removes the reference attribute value view.<p>
851     *
852     * @param reference the reference view
853     * @param force <code>true</code> if the widget should be removed even if it is the last one
854     */
855    public void removeAttributeValue(CmsAttributeValueView reference, boolean force) {
856
857        CmsAttributeHandler parentHandler = null;
858        CmsAttributeValueView parentView = null;
859        boolean removeParent = false;
860
861        CmsEntityAttribute attribute = m_entity.getAttribute(m_attributeName);
862        if (isChoiceHandler() && attribute.isSingleValue()) {
863            // removing last choice value, so remove choice itself
864            parentHandler = (CmsAttributeHandler)m_parentHandler;
865            parentView = reference.getParentView();
866            removeParent = true;
867        }
868
869        if (attribute.isSingleValue() && !force) {
870
871            reference.removeValue();
872            if (!attribute.isSimpleValue()) {
873                removeHandlers(0);
874            }
875            m_entity.removeAttribute(m_attributeName);
876        } else {
877            int index = reference.getValueIndex();
878            if (attribute.isComplexValue()) {
879                removeHandlers(index);
880                CmsEntity value = attribute.getComplexValues().get(index);
881                m_entity.removeAttributeValue(m_attributeName, index);
882                m_entityBackEnd.removeEntity(value.getId());
883            } else {
884                m_entity.removeAttributeValue(m_attributeName, index);
885            }
886            reference.removeFromParent();
887            m_attributeValueViews.remove(reference);
888
889        }
890        updateButtonVisisbility();
891        if (removeParent && (parentHandler != null) && (parentView != null)) {
892            parentHandler.removeAttributeValue(parentView);
893            parentView.setCollapsed(false);
894        }
895        CmsUndoRedoHandler handler = CmsUndoRedoHandler.getInstance();
896        if (handler.isIntitalized()) {
897            handler.addChange(m_entity.getId(), m_attributeName, 0, ChangeType.remove);
898        }
899
900    }
901
902    /**
903     * Removes the attribute value from the given index, also manipulating the editor DOM to display the change.<p>
904     *
905     * @param valueIndex the value index
906     */
907    public void removeAttributeValue(int valueIndex) {
908
909        if (m_attributeValueViews.size() > valueIndex) {
910            removeAttributeValue(m_attributeValueViews.get(valueIndex));
911        }
912    }
913
914    /**
915     * Removes the attribute value (and corresponding widget) with the given index, and returns
916     * the parent widget.<p>
917     *
918     * @param valueIndex the value index
919     * @param force <code>true</code> if the widget should be removed even if it is the last one
920     *
921     * @return the parent widget
922     */
923    public Panel removeAttributeValueAndReturnPrevParent(int valueIndex, boolean force) {
924
925        if (m_attributeValueViews.size() > valueIndex) {
926            CmsAttributeValueView view = m_attributeValueViews.get(valueIndex);
927            Panel result = (Panel)view.getParent();
928            removeAttributeValue(view, force);
929            return result;
930        }
931        return null;
932
933    }
934
935    /**
936     * Removes the attribute value with the given index.<p>
937     * This will not execute any DOM manipulations.<p>
938     *
939     * @param valueIndex the index of the attribute value to remove
940     */
941    public void removeAttributeValueFromEntity(int valueIndex) {
942
943        CmsEntityAttribute attribute = m_entity.getAttribute(m_attributeName);
944        if (attribute.isSingleValue()) {
945            if (attribute.isComplexValue()) {
946                removeHandlers(0);
947            }
948            m_entity.removeAttribute(m_attributeName);
949        } else {
950            if (attribute.isComplexValue()) {
951                removeHandlers(valueIndex);
952                CmsEntity value = attribute.getComplexValues().get(valueIndex);
953                m_entity.removeAttributeValue(m_attributeName, valueIndex);
954                m_entityBackEnd.removeEntity(value.getId());
955            } else {
956                m_entity.removeAttributeValue(m_attributeName, valueIndex);
957            }
958        }
959    }
960
961    /**
962     * Removes validation messages for all views associated with this attribute handler.<p>
963     */
964    public void removeValidationMessages() {
965
966        for (CmsAttributeValueView view : m_attributeValueViews) {
967            view.removeValidationMessage();
968        }
969    }
970
971    /**
972     * Sets the error message for the given value index.<p>
973     *
974     * @param valueIndex the value index
975     * @param message the error message
976     * @param tabbedPanel the forms tabbed panel if available
977     */
978    public void setErrorMessage(int valueIndex, String message, CmsTabbedPanel<?> tabbedPanel) {
979
980        if (!m_attributeValueViews.isEmpty()) {
981            FlowPanel parent = (FlowPanel)m_attributeValueViews.get(0).getParent();
982            CmsAttributeValueView valueView = (CmsAttributeValueView)parent.getWidget(valueIndex);
983            valueView.setErrorMessage(message);
984            if (tabbedPanel != null) {
985                int tabIndex = tabbedPanel.getTabIndex(valueView.getElement());
986                if (tabIndex > -1) {
987                    Widget tab = tabbedPanel.getTabWidget(tabIndex);
988                    tab.setTitle("This tab has errors.");
989                    tab.getParent().removeStyleName(I_CmsLayoutBundle.INSTANCE.form().hasWarning());
990                    tab.getParent().addStyleName(I_CmsLayoutBundle.INSTANCE.form().hasError());
991                }
992
993            }
994        }
995    }
996
997    /**
998     * @see org.opencms.acacia.client.CmsRootHandler#setHandlerById(java.lang.String, org.opencms.acacia.client.CmsAttributeHandler)
999     */
1000    @Override
1001    public void setHandlerById(String attributeName, CmsAttributeHandler handler) {
1002
1003        if (m_parentHandler != null) {
1004            m_parentHandler.setHandlerById(attributeName, handler);
1005        }
1006    }
1007
1008    /**
1009     * Sets the parent attribute handler.<p>
1010     *
1011     * @param handler the parent attribute handler
1012     */
1013    public void setParentHandler(I_CmsAttributeHandler handler) {
1014
1015        m_parentHandler = handler;
1016    }
1017
1018    /**
1019     * Sets the warning message for the given value index.<p>
1020     *
1021     * @param valueIndex the value index
1022     * @param message the warning message
1023     * @param tabbedPanel the forms tabbed panel if available
1024     */
1025    public void setWarningMessage(int valueIndex, String message, CmsTabbedPanel<?> tabbedPanel) {
1026
1027        if (!m_attributeValueViews.isEmpty()) {
1028            FlowPanel parent = (FlowPanel)m_attributeValueViews.get(0).getParent();
1029            CmsAttributeValueView valueView = (CmsAttributeValueView)parent.getWidget(valueIndex);
1030            valueView.setWarningMessage(message);
1031            if (tabbedPanel != null) {
1032                int tabIndex = tabbedPanel.getTabIndex(valueView.getElement());
1033                if (tabIndex > -1) {
1034                    Widget tab = tabbedPanel.getTabWidget(tabIndex);
1035                    tab.setTitle("This tab has warnings.");
1036                    tab.getParent().addStyleName(I_CmsLayoutBundle.INSTANCE.form().hasWarning());
1037                }
1038
1039            }
1040        }
1041    }
1042
1043    /**
1044     * Updates the add, remove and sort button visibility on the given inline widget or all registered attribute value views.<p>
1045     *
1046     * @param inlineWidget the inline widget
1047     */
1048    public void updateButtonVisibilty(CmsInlineEntityWidget inlineWidget) {
1049
1050        int minOccurrence = 0;
1051        int maxOccurrence = 0;
1052        if (isChoiceHandler()) {
1053            minOccurrence = 0;
1054            maxOccurrence = getEntityType().getChoiceMaxOccurrence();
1055        } else {
1056            minOccurrence = getEntityType().getAttributeMinOccurrence(m_attributeName);
1057            maxOccurrence = getEntityType().getAttributeMaxOccurrence(m_attributeName);
1058        }
1059        CmsEntityAttribute attribute = m_entity.getAttribute(m_attributeName);
1060        boolean mayHaveMore = (maxOccurrence > minOccurrence)
1061            && ((((attribute == null) && (!getAttributeType().isSimpleType() || (inlineWidget != null)))
1062                || ((attribute != null) && (attribute.getValueCount() < maxOccurrence))));
1063        boolean needsRemove = false;
1064        boolean needsSort = false;
1065        if ((isChoiceHandler() || !getEntityType().isChoice()) && m_entity.hasAttribute(m_attributeName)) {
1066            int valueCount = m_entity.getAttribute(m_attributeName).getValueCount();
1067            needsRemove = (maxOccurrence > minOccurrence) && (valueCount > minOccurrence);
1068            needsSort = !isSingleValueHandler() && (valueCount > 1);
1069        }
1070        if (inlineWidget != null) {
1071            boolean mayEdit = (attribute != null) && (attribute.getValueCount() > inlineWidget.getAttributeIndex());
1072            inlineWidget.updateButtonVisibility(mayEdit, mayHaveMore, needsRemove, needsSort);
1073        } else {
1074            for (CmsAttributeValueView value : m_attributeValueViews) {
1075                value.updateButtonVisibility(mayHaveMore, needsRemove, needsSort);
1076            }
1077        }
1078    }
1079
1080    /**
1081     * Updates the add, remove and sort button visibility on all registered attribute value views.<p>
1082     */
1083    public void updateButtonVisisbility() {
1084
1085        updateButtonVisibilty(null);
1086    }
1087
1088    /**
1089     * Returns if the attribute handler is handling a single value only.<p>
1090     *
1091     * @return <code>true</code> if the attribute handler is handling a single value only
1092     */
1093    protected boolean isSingleValueHandler() {
1094
1095        return m_singleValueIndex > -1;
1096    }
1097
1098    /**
1099     * Sets the single value index.<p>
1100     *
1101     * @param valueIndex the value index
1102     */
1103    protected void setSingleValueIndex(int valueIndex) {
1104
1105        m_singleValueIndex = valueIndex;
1106    }
1107
1108    /**
1109     * Clears the inline styles used during move animation.<p>
1110     *
1111     * @param placeHolder the animation place holder
1112     * @param reference the moved attribute widget
1113     */
1114    void clearMoveAnimationStyles(Element placeHolder, CmsAttributeValueView reference) {
1115
1116        placeHolder.removeFromParent();
1117        reference.getElement().getParentElement().getStyle().clearPosition();
1118        reference.getElement().getStyle().clearPosition();
1119        reference.getElement().getStyle().clearWidth();
1120        reference.getElement().getStyle().clearZIndex();
1121        reference.showButtons();
1122    }
1123
1124    /**
1125     * Adds a new choice option.<p>
1126     *
1127     * @param reference the reference view
1128     * @param choicePath the choice attribute path
1129     */
1130    private void addChoiceOption(CmsAttributeValueView reference, List<String> choicePath) {
1131
1132        String attributeChoice = choicePath.get(0);
1133        CmsType optionType = getAttributeType().getAttributeType(attributeChoice);
1134        int valueIndex = reference.getValueIndex() + 1;
1135        CmsEntity choiceEntity = m_entityBackEnd.createEntity(null, getAttributeType().getId());
1136        CmsAttributeValueView valueWidget = reference;
1137        if (reference.hasValue()) {
1138            valueWidget = new CmsAttributeValueView(
1139                this,
1140                m_widgetService.getAttributeLabel(attributeChoice),
1141                m_widgetService.getAttributeHelp(attributeChoice));
1142            if (optionType.isSimpleType() && m_widgetService.isDisplaySingleLine(attributeChoice)) {
1143                valueWidget.setCompactMode(CmsAttributeValueView.COMPACT_MODE_SINGLE_LINE);
1144            }
1145        }
1146
1147        List<CmsChoiceMenuEntryBean> menuEntries = CmsRenderer.getChoiceEntries(getAttributeType(), true);
1148        for (CmsChoiceMenuEntryBean menuEntry : menuEntries) {
1149            valueWidget.addChoice(m_widgetService, menuEntry);
1150        }
1151
1152        m_entity.insertAttributeValue(m_attributeName, choiceEntity, valueIndex);
1153        ((FlowPanel)reference.getParent()).insert(valueWidget, valueIndex);
1154        insertHandlers(valueWidget.getValueIndex());
1155
1156        if (optionType.isSimpleType()) {
1157            String defaultValue = m_widgetService.getDefaultAttributeValue(attributeChoice, getSimplePath(valueIndex));
1158            I_CmsFormEditWidget widget = m_widgetService.getAttributeFormWidget(attributeChoice);
1159            choiceEntity.addAttributeValue(attributeChoice, defaultValue);
1160            valueWidget.setValueWidget(widget, defaultValue, defaultValue, true);
1161        } else {
1162            CmsEntity value = m_entityBackEnd.createEntity(null, optionType.getId());
1163            choiceEntity.addAttributeValue(attributeChoice, value);
1164            List<String> remainingAttributeNames = tail(choicePath);
1165            createNestedEntitiesForChoicePath(value, remainingAttributeNames);
1166            I_CmsEntityRenderer renderer = m_widgetService.getRendererForAttribute(attributeChoice, optionType);
1167            valueWidget.setValueEntity(renderer, value);
1168        }
1169        updateButtonVisisbility();
1170
1171    }
1172
1173    /**
1174     * Adds a new complex value which corresponds to a choice element.<p>
1175     *
1176     * @param reference the reference view
1177     * @param choicePath the path of choice attribute names
1178     */
1179    private void addComplexChoiceValue(CmsAttributeValueView reference, List<String> choicePath) {
1180
1181        CmsEntity value = m_entityBackEnd.createEntity(null, getAttributeType().getId());
1182        CmsEntity parentValue = value;
1183        for (String attributeChoice : choicePath) {
1184            CmsType choiceType = m_entityBackEnd.getType(parentValue.getTypeName()).getAttributeType(
1185                CmsType.CHOICE_ATTRIBUTE_NAME);
1186            CmsEntity choice = m_entityBackEnd.createEntity(null, choiceType.getId());
1187            parentValue.addAttributeValue(CmsType.CHOICE_ATTRIBUTE_NAME, choice);
1188            CmsType choiceOptionType = choiceType.getAttributeType(attributeChoice);
1189            if (choiceOptionType.isSimpleType()) {
1190                String choiceValue = m_widgetService.getDefaultAttributeValue(attributeChoice, getSimplePath(0));
1191                choice.addAttributeValue(attributeChoice, choiceValue);
1192                break;
1193            } else {
1194                CmsEntity choiceValue = m_entityBackEnd.createEntity(null, choiceOptionType.getId());
1195                choice.addAttributeValue(attributeChoice, choiceValue);
1196                parentValue = choiceValue;
1197            }
1198        }
1199        insertValueAfterReference(value, reference);
1200        if (getMaxOccurence() == 1) {
1201            reference.setCollapsed(true);
1202        }
1203    }
1204
1205    /**
1206     * Changes the attribute value.<p>
1207     *
1208     * @param valueIndex the attribute value index
1209     * @param value the value
1210     */
1211    private void changeEntityValue(String value, int valueIndex) {
1212
1213        if (getEntityType().isChoice()) {
1214            CmsEntity choice = m_entity.getAttribute(CmsType.CHOICE_ATTRIBUTE_NAME).getComplexValues().get(valueIndex);
1215            String attributeName = getChoiceName(valueIndex);
1216            if (attributeName != null) {
1217                choice.setAttributeValue(attributeName, value, 0);
1218            }
1219        } else {
1220            m_entity.setAttributeValue(m_attributeName, value, valueIndex);
1221        }
1222    }
1223
1224    /**
1225     * Returns the attribute choice name for the given index.<p>
1226     *
1227     * @param valueIndex the value index
1228     *
1229     * @return the attribute choice name
1230     */
1231    private String getChoiceName(int valueIndex) {
1232
1233        if (isChoiceHandler()) {
1234            CmsEntity choice = m_entity.getAttribute(CmsType.CHOICE_ATTRIBUTE_NAME).getComplexValues().get(valueIndex);
1235            if (choice != null) {
1236                for (String option : getAttributeType().getAttributeNames()) {
1237                    if (choice.hasAttribute(option)) {
1238                        return option;
1239
1240                    }
1241                }
1242            }
1243        }
1244        return null;
1245    }
1246
1247    /**
1248     * Returns the entity type.<p>
1249     *
1250     * @return the entity type
1251     */
1252    private CmsType getEntityType() {
1253
1254        if (m_entityType == null) {
1255            m_entityType = m_entityBackEnd.getType(m_entity.getTypeName());
1256        }
1257        return m_entityType;
1258    }
1259
1260    /**
1261     * Inserts an entity value after the given reference.<p>
1262     *
1263     * @param value the entity value
1264     * @param reference the reference
1265     */
1266    private void insertValueAfterReference(CmsEntity value, CmsAttributeValueView reference) {
1267
1268        int valueIndex = -1;
1269        if (reference.getElement().getNextSiblingElement() == null) {
1270            m_entity.addAttributeValue(m_attributeName, value);
1271        } else {
1272            valueIndex = reference.getValueIndex() + 1;
1273            m_entity.insertAttributeValue(m_attributeName, value, valueIndex);
1274        }
1275        CmsAttributeValueView valueWidget = reference;
1276        if (reference.hasValue()) {
1277            valueWidget = new CmsAttributeValueView(
1278                this,
1279                m_widgetService.getAttributeLabel(m_attributeName),
1280                m_widgetService.getAttributeHelp(m_attributeName));
1281            CmsRenderer.setAttributeChoice(m_widgetService, valueWidget, getAttributeType());
1282            if (valueIndex == -1) {
1283                ((FlowPanel)reference.getParent()).add(valueWidget);
1284                m_attributeValueViews.remove(valueWidget);
1285                m_attributeValueViews.add(valueWidget);
1286            } else {
1287                ((FlowPanel)reference.getParent()).insert(valueWidget, valueIndex);
1288                m_attributeValueViews.remove(valueWidget);
1289                m_attributeValueViews.add(valueIndex, valueWidget);
1290                m_widgetService.addChangedOrderPath(getSimplePath(-1));
1291            }
1292
1293        }
1294        valueIndex = valueWidget.getValueIndex();
1295        insertHandlers(valueIndex);
1296        I_CmsEntityRenderer renderer = m_widgetService.getRendererForAttribute(m_attributeName, getAttributeType());
1297        valueWidget.setValueEntity(renderer, value);
1298    }
1299
1300    /**
1301     * Creates a list consisting of all but the first element of another list.<p>
1302     *
1303     * @param values the list
1304     *
1305     * @return the tail of the list
1306     */
1307    private List<String> tail(List<String> values) {
1308
1309        List<String> result = new ArrayList<String>();
1310        boolean first = true;
1311        for (String value : values) {
1312            if (!first) {
1313                result.add(value);
1314            }
1315            first = false;
1316        }
1317        return result;
1318
1319    }
1320}