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