/*
 * Copyright 2003 - 2017 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.components.help;

import java.util.UUID;

import org.efaps.admin.program.esjp.EFapsClassLoader;
import org.efaps.admin.ui.Command;
import org.efaps.api.ui.IHelpProvider;
import org.efaps.ui.wicket.models.objects.UIMenuItem;
import org.efaps.ui.wicket.util.Configuration;
import org.efaps.ui.wicket.util.Configuration.ConfigAttribute;
import org.efaps.util.EFapsBaseException;
import org.efaps.util.UUIDUtil;
import org.efaps.util.cache.CacheReloadException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class Help.
 *
 * @author The eFaps Team
 */
public final class HelpUtil
{
    /**
     * Logging instance used in this class.
     */
    private static final Logger LOG = LoggerFactory.getLogger(HelpUtil.class);

    /**
     * Instantiates a new help util.
     */
    private HelpUtil()
    {
    }

    /**
     * Checks for help.
     *
     * @param _cmdId the cmd id
     * @return true, if successful
     */
    public static boolean hasHelp(final Long _cmdId)
    {
        boolean ret = false;
        try {
            final Class<?> clazz = Class.forName(Configuration.getAttribute(Configuration.ConfigAttribute.HELPSNIPPROV),
                            false, EFapsClassLoader.getInstance());
            final IHelpProvider provider = (IHelpProvider) clazz.newInstance();
            ret = provider.hasHelp(_cmdId);
        } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException
                        | EFapsBaseException e) {
            HelpUtil.LOG.error("ClassNotFoundException", e);
        }
        return ret;
    }

    /**
     * Checks for help.
     *
     * @return true, if successful
     */
    public static boolean isHelpAdmin()
    {
        boolean ret = false;
        try {
            final Class<?> clazz = Class.forName(Configuration.getAttribute(Configuration.ConfigAttribute.HELPSNIPPROV),
                            false, EFapsClassLoader.getInstance());
            final IHelpProvider provider = (IHelpProvider) clazz.newInstance();
            ret = provider.isHelpAdmin();
        } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException
                        | EFapsBaseException e) {
            HelpUtil.LOG.error("ClassNotFoundException", e);
        }
        return ret;
    }

    /**
     * Checks for help.
     *
     * @return true, if successful
     */
    public static boolean isEditMode()
    {
        boolean ret = false;
        try {
            final Class<?> clazz = Class.forName(Configuration.getAttribute(Configuration.ConfigAttribute.HELPSNIPPROV),
                            false, EFapsClassLoader.getInstance());
            final IHelpProvider provider = (IHelpProvider) clazz.newInstance();
            ret = provider.isEditMode();
        } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException
                        | EFapsBaseException e) {
            HelpUtil.LOG.error("ClassNotFoundException", e);
        }
        return ret;
    }

    /**
     * Gets the help.
     *
     * @param _cmdId the cmd id
     * @return the help
     */
    public static CharSequence getHelp(final long _cmdId)
    {
        CharSequence ret;
        try {
            final Class<?> clazz = Class.forName(Configuration.getAttribute(Configuration.ConfigAttribute.HELPSNIPPROV),
                            false, EFapsClassLoader.getInstance());
            final IHelpProvider provider = (IHelpProvider) clazz.newInstance();
            ret = provider.getHelp(_cmdId);
        } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException
                        | EFapsBaseException e) {
            HelpUtil.LOG.error("ClassNotFoundException", e);
            ret = "";
        }
        return ret;
    }

    /**
     * Gets the menu item.
     *
     * @return the menu item
     * @throws CacheReloadException the cache reload exception
     */
    public static UIMenuItem getHelpMenuItem()
    {
        UIMenuItem ret = null;
        try {
            final String cmdStr = Configuration.getAttribute(ConfigAttribute.HELPEDITCMD);
            final Command cmd = UUIDUtil.isUUID(cmdStr) ? Command.get(UUID.fromString(cmdStr)) : Command.get(cmdStr);
            ret = new UIMenuItem(cmd.getUUID());
        } catch (final EFapsBaseException e) {
            HelpUtil.LOG.error("ClassNotFoundException", e);
        }
        return ret;
    }
}


/*
 * Copyright 2003 - 2014 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

package org.efaps.ui.wicket.models.objects;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.swing.tree.DefaultMutableTreeNode;

import org.apache.wicket.RestartResponseException;
import org.efaps.admin.AbstractAdminObject;
import org.efaps.admin.datamodel.Attribute;
import org.efaps.admin.datamodel.Type;
import org.efaps.admin.datamodel.ui.UIValue;
import org.efaps.admin.dbproperty.DBProperties;
import org.efaps.admin.event.EventDefinition;
import org.efaps.admin.event.EventType;
import org.efaps.admin.event.Parameter.ParameterValues;
import org.efaps.admin.event.Return;
import org.efaps.admin.event.Return.ReturnValues;
import org.efaps.admin.ui.AbstractCommand;
import org.efaps.admin.ui.AbstractCommand.SortDirection;
import org.efaps.admin.ui.AbstractUserInterfaceObject.TargetMode;
import org.efaps.admin.ui.Image;
import org.efaps.admin.ui.Menu;
import org.efaps.admin.ui.field.Field;
import org.efaps.api.ci.UITableFieldProperty;
import org.efaps.beans.ValueList;
import org.efaps.beans.valueparser.ParseException;
import org.efaps.beans.valueparser.ValueParser;
import org.efaps.db.Context;
import org.efaps.db.Instance;
import org.efaps.db.MultiPrintQuery;
import org.efaps.db.PrintQuery;
import org.efaps.db.SelectBuilder;
import org.efaps.ui.wicket.models.field.AbstractUIField;
import org.efaps.ui.wicket.models.field.FieldConfiguration;
import org.efaps.ui.wicket.models.field.IAutoComplete;
import org.efaps.ui.wicket.models.field.IHidden;
import org.efaps.ui.wicket.models.field.UIField;
import org.efaps.ui.wicket.pages.error.ErrorPage;
import org.efaps.util.EFapsException;
import org.efaps.util.cache.CacheReloadException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class is used to provide the Model for the StructurBrowser for eFpas.

 * It is used in tow different cases. In one case it is a TreeTable, where the
 * Table will be provided with additional information in columns. In the other
 * case a Tree only.

 * The concept of this class is to provide a Model which connects through the
 * eFaps-kernel to the eFaps-DataBase and turn it to a Standard TreeModel from
 * javax.swing.tree. which will be used from the Component to
 * render the Tree and the Table. This leads to a very similar behavior of the
 * WebApp GUI to a swing GUI.

 * This model works asyncron. That means only the actually in the GUI rendered
 * Nodes (and Columns) will be retrieved from the eFaps-DataBase. The next level
 * in a tree will be retrieved on the expand of a TreeNode. To achieve this and
 * to be able to render expand-links for every node it will only be checked if
 * it is a potential parent (if it has children). In the case of expanding this
 * Node the children will be retrieved and rendered.

 * To access the eFaps-Database a esjp is used, which will be used in five
 * different cases. To distinguish the use of the esjp some extra Parameters
 * will be passed to the esjp when calling it.
 *
 * @author The eFaps Team
 * @version $Id$
 */
public class UIStructurBrowser
    extends AbstractUIHeaderObject
    implements IPageObject
{
    /**
     * Enum is used to set for this UIStructurBrowser which status of execution
     * it is in.
     */
    public enum ExecutionStatus {
        /** Method addChildren is executed. */
        ADDCHILDREN,
        /** Method addChildren is executed. */
        ALLOWSCHILDREN,
        /** Method is executed to check if the in case of edit the creation of items is allowed. */
        ALLOWSITEM,
        /** Method checkForChildren is executed. */
        CHECKFORCHILDREN,
        /** Method is creating a new folder. */
        CHECKHIDECOLUMN4ROW,
        /**
         * Method is called after the insert etc. of a new node in edit mode to
         * get a JavaScript that will be appended to the AjaxTarget. In this
         * Step the values for the StructurBrowsers also can be altered.
         * @see {@link UIStructurBrowser.#setValuesFromUI(Map, DefaultMutableTreeNode)setValuesFromUI}
         */
        GETJAVASCRIPT4TARGET,
        /** Method execute is executed. */
        EXECUTE,
        /**
         * Executed on removal of a node as an listener and does not
         * effect directly the tree, but allows to manipulate it.
         */
        NODE_REMOVE,
        /**
         * Executed on insert of an new item node as an listener and does not
         * effect directly the tree, but allows to manipulate it.
         */
        NODE_INSERTITEM,
        /**
         * Executed on insert of an item as child as an listener and does not
         * effect directly the tree, but allows to manipulate it.
         */
        NODE_INSERTCHILDITEM,
        /**
         * Executed on insert of an folder as child as an listener and does not
         * effect directly the tree, but allows to manipulate it.
         */
        NODE_INSERTCHILDFOLDER,
        /** Method sort is executed. */
        SORT;
    }

    /**
     * Static part of the key to get the Information stored in the session
     * in relation to this StruturBrowser.
     */
    public static final String USERSESSIONKEY = UIStructurBrowser.class.getName() + ".SessionKey";

    /**
     * Logging instance used in this class.
     */
    private static final Logger LOG = LoggerFactory.getLogger(UIStructurBrowser.class);

    /**
     * Needed for serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     *  This instance variable holds if this StructurBrowserModel can have
     *  children at all.
     */
    private boolean allowChildren;

    /**
     *  This instance variable holds if this StructurBrowserModel can have
     *  children of type items, will onlye be evaluated if allowChils is true.
     */
    private boolean allowItems;

    /**
     * This instance variable holds if this StructurBrowserModel is a
     * parent, this is needed because, first it will be only determined if a
     * node is a potential parent, and later on the children will be retrieved
     * from the eFaps-DataBase.
     *
     * @see #isParent()
     */
    private boolean parent;

    /**
     * This instance variable holds the children of this StructurBrowserModel.
     */
    private final List<UIStructurBrowser> children = new ArrayList<>();

    /**
     * Holds the columns in case of a TableTree.
     */
    private final List<AbstractUIField> columns = new ArrayList<>();

    /**
     * Holds the hidden columns in case of a TableTree.
     */
    private final List<IHidden> hidden = new ArrayList<>();

    /**
     * Holds the label of the Node which will be presented in the GUI.
     *
     * @see #toString()
     * @see #getLabel()
     * @see #setLabel(String)
     * @see #requeryLabel()
     */
    private String label;

    /**
     * Holds the Name of the Field the StructurBrowser should be in, in case of
     * a TableTree.
     */
    private String browserFieldName;

    /**
     * This instance variable holds, if this StructurBrowserModel is the Root of
     * a tree.
     */
    private final boolean root;

    /**
     * Holds the Value for the Label as it is difined in the DBProperties.
     */
    private String valueLabel;

    /**
     * Contains the url for the Image that will be presented in GUI.
     */
    private String image;

    /**
     * this instrance variable is used as a TriState, to determine if the
     * Model should show the direction of this Model as Child in comparisment to
     * the parent.

     * The tristate is used as follows:
null: no direction will be
     * shown

true: an arrow showing downwards, will be rendered

     *
false: an arrow showing upwards, will be rendered

     */
    private Boolean direction = null;

    /**
     * Stores the actual execution status.
     */
    private ExecutionStatus executionStatus;

    /**
     * Is this model expanded.
     */
    private boolean expanded;

    /**
     * The index of the column containing the browser.
     */
    private int browserFieldIndex;

    /**
     * If true the tree is always expanded and the inks for expand and
     * collapse will not work.
     */
    private boolean forceExpanded = false;

    /**
     * Level of the current instance.
     */
    private int level = 0;

    /**
     * The parent UIStructurBrowser. Used to be able to climb up in the hirachy.
     */
    private UIStructurBrowser parentBrws = null;

    /**
     * Set of expanded UIStructurBrowser. Used on ly for the root node to be able
     * to initialize correctly the Tree for the UserInterface.
     */
    private final Set<UIStructurBrowser> expandedBrowsers = new HashSet<>();

    /** The page position. */
    private PagePosition pagePosition;

    /**
     * Standard constructor, if called this StructurBrowserModel will be defined
     * as root.
     *
     * @param _commandUUID UUID of the calling command
     * @param _instanceKey oid
     * @throws EFapsException on error
     *
     */
    public UIStructurBrowser(final UUID _commandUUID,
                             final String _instanceKey)
        throws EFapsException
    {
        this(_commandUUID, _instanceKey, true, SortDirection.ASCENDING);
    }

    /**
     * Internal constructor, it is used to set that this StructurBrowserModel is
     * not a root.
     *
     * @param _commandUUID UUID of the command
     * @param _instanceKey OID
     * @param _root is this STrtucturbrowser the root
     * @param _sortdirection sort direction
     * @throws EFapsException on error
     */
    protected UIStructurBrowser(final UUID _commandUUID,
                                final String _instanceKey,
                                final boolean _root,
                                final SortDirection _sortdirection)
        throws EFapsException
    {
        super(_commandUUID, _instanceKey);
        this.root = _root;
        if (isRoot()) {
            this.allowChildren = true;
        }
        setSortDirectionInternal(_sortdirection);
        initialise();
    }

    /**
     * Internal method to call a constructor, it is used to set that this
     * StructurBrowserModel is not a root.
     *
     * @param _instance     Instance
     * @param _strucBrwsr   StructurBrowser the values will be copied from
     * @return UIStructurBrowser
     * @throws EFapsException on error
     */
    protected UIStructurBrowser getNewStructurBrowser(final Instance _instance,
                                                      final UIStructurBrowser _strucBrwsr)
        throws EFapsException
    {
        final UUID uuid;
        if (_strucBrwsr.getTable() == null) {
            uuid = Menu.getTypeTreeMenu(_instance.getType()).getUUID();
        } else {
            uuid = _strucBrwsr.getCommandUUID();
        }
        final UIStructurBrowser ret = new UIStructurBrowser(uuid, _instance == null ? null : _instance.getKey(), false,
                        _strucBrwsr.getSortDirection());
        ret.setParentBrws(this);
        ret.setLevel(getLevel() + 1);
        return ret;
    }

    /**
     * Method used to initialize this StructurBrowserModel.
     *
     * @throws EFapsException on error
     */
    protected void initialise()
        throws EFapsException
    {
        final AbstractCommand command = getCommand();
        if (command != null && command.getTargetTable() != null) {
            setTableUUID(command.getTargetTable().getUUID());
            this.browserFieldName = command.getTargetStructurBrowserField();
            setShowCheckBoxes(command.isTargetShowCheckBoxes());
        } else if (getInstance() != null) {
            final String tmplabel = Menu.getTypeTreeMenu(getInstance().getType()).getLabel();
            this.valueLabel = DBProperties.getProperty(tmplabel);
        }

        // set default sort
        if (command.getTargetTableSortKey() != null) {
            setSortKeyInternal(command.getTargetTableSortKey());
            setSortDirection(command.getTargetTableSortDirection());
        }

        // get the User specific Attributes if exist overwrite the defaults
        try {
            if (Context.getThreadContext().containsUserAttribute(
                            getCacheKey(UITable.UserCacheKey.SORTKEY))) {
                setSortKeyInternal(Context.getThreadContext().getUserAttribute(
                                getCacheKey(UITable.UserCacheKey.SORTKEY)));
            }
            if (Context.getThreadContext().containsUserAttribute(
                            getCacheKey(UITable.UserCacheKey.SORTDIRECTION))) {
                setSortDirection(SortDirection.getEnum(Context.getThreadContext()
                                .getUserAttribute(getCacheKey(UITable.UserCacheKey.SORTDIRECTION))));
            }
        } catch (final EFapsException e) {
            // we don't throw an error because this are only Usersettings
            UIStructurBrowser.LOG.error("error during the retrieve of UserAttributes", e);
        }
    }

    /**
     * This method should be called to actually execute this
     * StructurBrowserModel, that means to retrieve the values from the
     * eFaps-DataBase, create the TreeModel etc. This method actually calls
     * depending if we have a Tree or a TreeTabel the Methodes
     * {@link #executeTree(List)} or {@link #executeTreeTable(List)}
     *
     * @see #executeTree(List)
     * @see #executeTreeTable(List)
     */
    @Override
    @SuppressWarnings("unchecked")
    public void execute()
    {
        setExecutionStatus(UIStructurBrowser.ExecutionStatus.EXECUTE);
        final List<Return> ret;
        try {
            if (getTableUUID() == null) {
                final Map<Instance, Boolean> map = new LinkedHashMap<>();
                map.put(getInstance(), null);
                executeTree(map, false);
            } else {
                final Map<Instance, Boolean> map = new LinkedHashMap<>();
                if (!isCreateMode()) {
                    ret = getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE, ParameterValues.CLASS, this,
                                ParameterValues.INSTANCE, getInstance());
                    map.putAll((Map<Instance, Boolean>) ret.get(0).get(ReturnValues.VALUES));
                }
                executeTreeTable(map, false);
            }
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
    }

    /**
     * This method is called in case of a Tree from the {@link #execute()}method
     * to fill this StructurBrowserModel with live.
     *
     * @param _map      List of Object
     * @param _expand   inside an expand
     */
    protected void executeTree(final Map<Instance, Boolean> _map,
                               final boolean _expand)
    {
        try {
            final List<Instance> instances = new ArrayList<>();
            for (final Instance inst : _map.keySet()) {
                instances.add(inst);
            }
            final ValueParser parser = new ValueParser(new StringReader(this.valueLabel));
            final ValueList valuelist = parser.ExpressionString();
            final MultiPrintQuery print = new MultiPrintQuery(instances);
            valuelist.makeSelect(print);
            print.execute();
            while (print.next()) {
                Object value = null;
                final Instance instance = print.getCurrentInstance();
                value = valuelist.makeString(getInstance(), print, getMode());
                final UIStructurBrowser child = getNewStructurBrowser(instance, this);
                this.children.add(child);
                child.setDirection(_map.get(instance));
                child.setLabel(value.toString());
                child.setAllowChildren(checkForAllowChildren(instance));
                if (isAllowChildren()) {
                    child.setParent(checkForChildren(instance));
                }
                child.setImage(Image.getTypeIcon(instance.getType()) != null ? Image.getTypeIcon(instance.getType())
                                .getName() : null);
            }
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        } catch (final ParseException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
        sortModel();
        expand(_expand);
        super.setInitialized(true);
    }

    /**
     * This method is called in case of a TreeTable from the {@link #execute()}
     * method to fill this StructurBrowserModel with live.
     *
     * @param _map      List of Objects
     * @param _expand   inside an expand
     */
    protected void executeTreeTable(final Map<Instance, Boolean> _map,
                                    final boolean _expand)
    {
        try {
            final List<Instance> instances = new ArrayList<>();
            for (final Instance inst : _map.keySet()) {
                instances.add(inst);
            }
            final List<Integer> userWidthList = getUserWidths();
            // evaluate for all expressions in the table
            final MultiPrintQuery multi = new MultiPrintQuery(instances);
            Type type = instances.isEmpty() ? null : instances.get(0).getType();
            int i = 0;
            for (final Field field : getUserSortedColumns()) {
                Attribute attr = null;
                if (field.hasAccess(getMode(), getInstance()) && !field.isNoneDisplay(getMode())) {
                    if (_map.size() > 0) {
                        if (field.getSelect() != null) {
                            multi.addSelect(field.getSelect());
                        } else if (field.getAttribute() != null) {
                            multi.addAttribute(field.getAttribute());
                        } else if (field.getPhrase() != null) {
                            multi.addPhrase(field.getName(), field.getPhrase());
                        } else if (field.getMsgPhrase() != null) {
                            multi.addMsgPhrase(new SelectBuilder(getBaseSelect4MsgPhrase(field)), field.getMsgPhrase());
                        }
                        if (field.getSelectAlternateOID() != null) {
                            multi.addSelect(field.getSelectAlternateOID());
                        }
                        if (field.containsProperty(UITableFieldProperty.SORT_SELECT)) {
                            multi.addSelect(field.getProperty(UITableFieldProperty.SORT_SELECT));
                        } else if (field.containsProperty(UITableFieldProperty.SORT_PHRASE)) {
                            multi.addPhrase(field.getProperty(UITableFieldProperty.SORT_PHRASE),
                                            field.getProperty(UITableFieldProperty.SORT_PHRASE));
                        } else if (field.containsProperty(UITableFieldProperty.SORT_MSG_PHRASE)) {
                            multi.addMsgPhrase(field.getProperty(UITableFieldProperty.SORT_MSG_PHRASE));
                        }
                    }
                    if (field.getAttribute() != null && type != null) {
                        attr = type.getAttribute(field.getAttribute());
                    }
                    if (isRoot()) {
                        SortDirection sortdirection = SortDirection.NONE;
                        if (field.getName().equals(getSortKey())) {
                            sortdirection = getSortDirection();
                        }
                        if (!field.isHiddenDisplay(getMode())) {
                            final FieldConfiguration fieldConfig = new FieldConfiguration(field.getId());
                            final UITableHeader uiTableHeader = new UITableHeader(this, fieldConfig, sortdirection,
                                            attr);
                            getHeaders().add(uiTableHeader);
                            if (!fieldConfig.isFixedWidth()) {
                                if (userWidthList != null && userWidthList.size() > i) {
                                    if (isShowCheckBoxes() && userWidthList.size() > i + 1) {
                                        uiTableHeader.setWidth(userWidthList.get(i + 1));
                                    } else {
                                        uiTableHeader.setWidth(userWidthList.get(i));
                                    }
                                }
                                setWidthWeight(getWidthWeight() + fieldConfig.getWidthWeight());
                            }
                        }
                    }
                    i++;
                }
            }
            if (!multi.execute()) {
                type = getTypeFromEvent();
            }
            Attribute attr = null;
            while (multi.next()) {
                Instance instance = multi.getCurrentInstance();
                final UIStructurBrowser child = getNewStructurBrowser(instance, this);
                child.setDirection(_map.get(instance));
                for (final Field field : getUserSortedColumns()) {
                    instance = evaluateFieldInstance(multi, field);
                    if (field.hasAccess(getMode(), getInstance()) && !field.isNoneDisplay(getMode())) {
                        Object value = null;
                        Object sortValue = null;
                        attr = null;
                        if (field.getSelect() != null) {
                            value = multi.getSelect(field.getSelect());
                            attr = multi.getAttribute4Select(field.getSelect());
                        } else if (field.getAttribute() != null) {
                            value = multi.getAttribute(field.getAttribute());
                            attr = multi.getAttribute4Attribute(field.getAttribute());
                        } else if (field.getPhrase() != null) {
                            value = multi.getPhrase(field.getName());
                        } else if (field.getMsgPhrase() != null) {
                            value = multi.getMsgPhrase(new SelectBuilder(getBaseSelect4MsgPhrase(field)),
                                            field.getMsgPhrase());
                        }
                        if (field.containsProperty(UITableFieldProperty.SORT_SELECT)) {
                            sortValue = multi.getSelect(field.getProperty(UITableFieldProperty.SORT_SELECT));
                        } else if (field.containsProperty(UITableFieldProperty.SORT_PHRASE)) {
                            sortValue = multi.getPhrase(field.getProperty(UITableFieldProperty.SORT_PHRASE));
                        } else if (field.containsProperty(UITableFieldProperty.SORT_MSG_PHRASE)) {
                            sortValue = multi.getMsgPhrase(field.getProperty(UITableFieldProperty.SORT_MSG_PHRASE));
                        }

                        final UIField uiField = new UIField(this, instance.getKey(),
                                        UIValue.get(field, attr, value)
                                            .setInstance(instance)
                                            .setClassObject(this)
                                            .setCallInstance(getInstance())
                                            .setRequestInstances(multi.getInstanceList()));
                        uiField.setCompareValue(sortValue);

                        if (field.getName().equals(getBrowserFieldName())) {
                            child.setLabel(uiField.getValue().getReadOnlyValue(uiField.getParent().getMode()));
                            child.setAllowChildren(checkForAllowChildren(instance));
                            if (child.isAllowChildren()) {
                                child.setAllowItems(checkForAllowItems(instance));
                                child.setParent(checkForChildren(instance));
                            }
                            child.setImage(Image.getTypeIcon(instance.getType()) != null ? Image.getTypeIcon(
                                            instance.getType()).getName() : null);
                            child.setBrowserFieldIndex(child.getColumns().size());
                        }
                        if (field.isHiddenDisplay(getMode())) {
                            child.getHidden().add(uiField);
                        } else {
                            child.getColumns().add(uiField);
                        }
                    }
                }
                this.children.add(child);
                child.checkHideColumn4Row();
            }
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
        sortModel();
        expand(_expand);
        super.setInitialized(true);
    }

    /**
     * Getter method for the instance variable {@link #hidden}.
     *
     * @return value of instance variable {@link #hidden}
     */
    @Override
    public List<IHidden> getHidden()
    {
        return this.hidden;
    }

    /**
     * Expand the tree with the information from the Session.
     * @param _expand is this inside an expand
     */
    @SuppressWarnings("unchecked")
    protected void expand(final boolean _expand)
    {
        try {
            // only if the element was opened the first time e.g. reload etc.
            if ((isRoot() || _expand)
                       && (Context.getThreadContext().containsSessionAttribute(getCacheKey()) || this.forceExpanded)) {
                final Map<String, Boolean> sessMap = (Map<String, Boolean>) Context
                                .getThreadContext().getSessionAttribute(getCacheKey());
                for (final UIStructurBrowser uiChild : this.children) {
                    if (isForceExpanded() || sessMap == null || sessMap.containsKey(uiChild.getInstanceKey())) {
                        final Boolean expandedTmp = sessMap == null || isForceExpanded()
                                                ? true : sessMap.get(uiChild.getInstanceKey());
                        if (expandedTmp != null && expandedTmp && uiChild.isParent()) {
                            uiChild.setExecutionStatus(UIStructurBrowser.ExecutionStatus.ADDCHILDREN);
                            final List<Return> ret = getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE,
                                                ParameterValues.INSTANCE, uiChild.getInstance(),
                                                ParameterValues.CLASS, uiChild);
                            final Map<Instance, Boolean> map = (Map<Instance, Boolean>) ret.get(0).get(
                                            ReturnValues.VALUES);
                            uiChild.setExpanded(true);
                            uiChild.add2ExpandedBrowsers(uiChild);
                            if (uiChild.getTableUUID() == null) {
                                uiChild.executeTree(map, true);
                            } else {
                                uiChild.executeTreeTable(map, true);
                            }
                        }
                    }
                }
            }
        } catch (final EFapsException e) {
            UIStructurBrowser.LOG.error("Error retreiving Session info for StruturBrowser from Command with UUID: {}",
                            getCommandUUID(), e);
        }
    }

    /**
     * @return set of expanded browsers
     */
    public Set<UIStructurBrowser> getExpandedBrowsers()
    {
        final Set<UIStructurBrowser> ret;
        if (isRoot()) {
            ret = this.expandedBrowsers;
        } else {
            ret = getParentBrws().getExpandedBrowsers();
        }
        return ret;
    }

    /**
     * @param _structBrowser structurbrowser to be added to the expanded
     */
    protected void add2ExpandedBrowsers(final UIStructurBrowser _structBrowser)
    {
        if (isRoot()) {
            this.expandedBrowsers.add(_structBrowser);
            this.expanded = true;
        } else {
            getParentBrws().add2ExpandedBrowsers(_structBrowser);
        }
    }

    /**
     * Method to sort the data of this model. It calls an esjp for sorting.
     */
    protected void sortModel()
    {
        setExecutionStatus(UIStructurBrowser.ExecutionStatus.SORT);
        try {
            getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE, ParameterValues.CLASS, this);

            if (getSortDirection() == SortDirection.DESCENDING) {
                Collections.reverse(this.children);
            }
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
    }

    /**
     * Method to sort this model and all child models.
     */
    @Override
    public void sort()
    {
        sortModel();
        for (final UIStructurBrowser child : this.children) {
            child.sort();
        }
    }

    /**
     * Getter method for the instance variable {@link #forceExpanded}.
     *
     * @return value of instance variable {@link #forceExpanded}
     */
    public boolean isForceExpanded()
    {
        return this.forceExpanded;
    }

    /**
     * Setter method for instance variable {@link #forceExpanded}.
     *
     * @param _forceExpanded value for instance variable {@link #forceExpanded}
     */
    protected void setForceExpanded(final boolean _forceExpanded)
    {
        this.forceExpanded = _forceExpanded;
    }

    /**
     * Getter method for the instance variable {@link #allowChildren}.
     *
     * @return value of instance variable {@link #allowChildren}
     */
    public boolean isAllowChildren()
    {
        return this.allowChildren;
    }

    /**
     * Setter method for instance variable {@link #allowChildren}.
     *
     * @param _allowChildren value for instance variable {@link #allowChildren}
     */

    public void setAllowChildren(final boolean _allowChildren)
    {
        this.allowChildren = _allowChildren;
    }

    /**
     * Has this StructurBrowserModel childs.
     *
     * @return true if has children, else false
     */
    public boolean hasChildren()
    {
        return !this.children.isEmpty();
    }

    /**
     * Getter method for instance variable {@link #children}.
     *
     * @return value of instance variable {@link #children}
     */
    public List<UIStructurBrowser> getChildren()
    {
        return this.children;
    }

    /**
     * Getter method for the instance variable {@link #allowItems}.
     *
     * @return value of instance variable {@link #allowItems}
     */
    public boolean isAllowItems()
    {
        return this.allowItems;
    }

    /**
     * Setter method for instance variable {@link #allowItems}.
     *
     * @param _allowItems value for instance variable {@link #allowItems}
     */
    public void setAllowItems(final boolean _allowItems)
    {
        this.allowItems = _allowItems;
    }

    /**
     * Method used to evaluate the type for this table from the connected
     * events.
     *
     * @return type if found
     * @throws EFapsException on error
     */
    protected Type getTypeFromEvent()
        throws EFapsException
    {
        final List<EventDefinition> events =  getObject4Event().getEvents(EventType.UI_TABLE_EVALUATE);
        String typeName = null;
        if (events.size() > 1) {
            throw new EFapsException(this.getClass(), "execute4NoInstance.moreThanOneEvaluate");
        } else {
            final EventDefinition event = events.get(0);
            // test for basic or abstract types
            if (event.getProperty("Type") != null) {
                typeName = event.getProperty("Type");
            }
            // no type yet search alternatives
            if (typeName == null) {
                for (int i = 1; i < 100; i++) {
                    final String nameTmp = "Type" + String.format("%02d", i);
                    if (event.getProperty(nameTmp) != null) {
                        typeName = event.getProperty(nameTmp);
                    } else {
                        break;
                    }
                }
            }
        }
        return Type.get(typeName);
    }

    /**
     * Method is called from the StructurBrowser in edit mode before rendering
     * the columns for row to be able to hide the columns for different rows by
     * setting the cell model to hide.
     */
    public void checkHideColumn4Row()
    {
        setExecutionStatus(UIStructurBrowser.ExecutionStatus.CHECKHIDECOLUMN4ROW);
        try {
            getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE,
                            ParameterValues.INSTANCE, getInstance(),
                            ParameterValues.CLASS, this);
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
    }

    /**
     * This method should be called to add children to a Node in the Tree.

     * e.g. in a standard implementation the children would be added to the Tree
     * on the expand-Event of the tree. The children a retrieved from an esjp
     * with the EventType UI_TABLE_EVALUATE.
     */
    @SuppressWarnings("unchecked")
    public void addChildren()
    {
        setExecutionStatus(UIStructurBrowser.ExecutionStatus.ADDCHILDREN);
        final List<Return> ret;
        try {
            ret = getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE, ParameterValues.INSTANCE, getInstance(),
                            ParameterValues.CLASS, this);
            final Map<Instance, Boolean> map = (Map<Instance, Boolean>) ret.get(0).get(ReturnValues.VALUES);

            if (getTableUUID() == null) {
                executeTree(map, false);
            } else {
                executeTreeTable(map, false);
            }
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
    }

    /**
     * This method is used to check if a node has potential children.
     *
     * @param _instance Instance of a Node to be checked
     * @return true if this Node has children, else false
     */
    protected boolean checkForAllowChildren(final Instance _instance)
    {
        setExecutionStatus(UIStructurBrowser.ExecutionStatus.ALLOWSCHILDREN);
        try {
            final List<Return> ret = getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE,
                            ParameterValues.INSTANCE, _instance,
                            ParameterValues.CLASS, this);
            return ret.isEmpty() ? false : ret.get(0).get(ReturnValues.TRUE) != null;
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
    }

    /**
     * This method is used to execute a listener with a specific event.
     * @param _status status to be executed
     * @param _uiID2Oid UI Id 2 Oid mapping
     */
    public void executeListener(final ExecutionStatus _status,
                                final Map<String, String> _uiID2Oid)
    {
        setExecutionStatus(_status);
        try {
            getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE,
                            ParameterValues.INSTANCE, getInstance(),
                            ParameterValues.CLASS, this,
                            ParameterValues.OIDMAP4UI,  _uiID2Oid);
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
    }

    /**
     * This method is used to check if a node has potential children.
     *
     * @param _instance Instance of a Node to be checked
     * @return true if this Node has children, else false
     */
    public boolean checkForAllowItems(final Instance _instance)
    {
        setExecutionStatus(UIStructurBrowser.ExecutionStatus.ALLOWSITEM);
        try {
            final List<Return> ret = getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE,
                            ParameterValues.INSTANCE, _instance,
                            ParameterValues.CLASS, this);
            return ret.isEmpty() ? false : ret.get(0) == null ? false : ret.get(0).get(ReturnValues.TRUE) != null;
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
    }

    /**
     * This method is used to check if a node has potential children.
     *
     * @param _instance Instance of a Node to be checked
     * @return true if this Node has children, else false
     */
    protected boolean checkForChildren(final Instance _instance)
    {
        setExecutionStatus(UIStructurBrowser.ExecutionStatus.CHECKFORCHILDREN);
        try {
            final List<Return> ret = getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE,
                            ParameterValues.INSTANCE, _instance,
                            ParameterValues.CLASS, this);
            return ret.isEmpty() ? false : ret.get(0).get(ReturnValues.TRUE) != null;
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
    }

    /**
     * Method is called from the StructurBrowser in edit mode from
     * {@link #setValuesFromUI(Map, DefaultMutableTreeNode)} to get
     * additional JavaScript to be appended to the AjaxTarget.
     * @param _parameters   Parameter as send from the UserInterface
     * @return JavaScript for the UserInterface
     */
    protected String getJavaScript4Target(final Map<String, String[]> _parameters)
    {
        final String ret;
        setExecutionStatus(UIStructurBrowser.ExecutionStatus.GETJAVASCRIPT4TARGET);
        try {
            final List<Return> retList = getObject4Event().executeEvents(EventType.UI_TABLE_EVALUATE,
                                ParameterValues.INSTANCE, getInstance(),
                                ParameterValues.CLASS, this,
                                ParameterValues.PARAMETERS, _parameters);
            ret = (String) retList.get(0).get(ReturnValues.SNIPLETT);
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
        return ret;
    }

    /**
     * This is the getter method for the instance variable {@link #parent}.
     *
     * @return value of instance variable {@link #parent}
     */
    public boolean isParent()
    {
        return this.parent;
    }

    /**
     * This is the setter method for the instance variable {@link #parent}.
     *
     * @param _parent the parent to set
     */
    public void setParent(final boolean _parent)
    {
        this.parent = _parent;
    }

    /**
     * Method to reset the Model.
     *
     * @see org.efaps.ui.wicket.models.AbstractModel#resetModel()
     */
    @Override
    public void resetModel()
    {
        super.setInitialized(false);
        getHeaders().clear();
        this.children.clear();
    }

    /**
     * @param _parameters   Parameter as send from the UserInterface
     * @param _node         Node the _parameters were send from
     * @throws EFapsException on error
     * @return JavaScript for the UserInterface
     */
    public String setValuesFromUI(final Map<String, String[]> _parameters,
                                  final DefaultMutableTreeNode _node)
        throws EFapsException
    {
        final Enumeration<?> preOrdEnum = ((DefaultMutableTreeNode) _node.getRoot()).preorderEnumeration();
        int i = 0;
        while (preOrdEnum.hasMoreElements()) {
            final DefaultMutableTreeNode node = (DefaultMutableTreeNode) preOrdEnum.nextElement();
            if (!node.isRoot()) {
                final UIStructurBrowser uiObject = (UIStructurBrowser) node.getUserObject();
                if (uiObject != null) {
                    for (final AbstractUIField object : uiObject.getColumns()) {
                        final IAutoComplete cell = object;
                        final String[] values =  null;  //_parameters.get(cell.getName());
                        if (cell.isAutoComplete()) {
                          //  final String[] autoValues = _parameters.get(cell.getName() + "AutoComplete");
                          //  cell.setCellTitle(autoValues[i]);
                          //  cell.setInstanceKey(values[i]);
                        } else {
                            if (values != null) {
                           //     cell.setValueFromUI(values[i]);
                            }
                        }
                    }
                    i++;
                }
            }
        }
        return getJavaScript4Target(_parameters);
    }

    /**
     * Get the Value of a Column identified by the index of the Column.
     *
     * @param _index index of the Column
     * @return String with the Value of the Column
     */
    public AbstractUIField getColumnValue(final int _index)
    {
        return this.columns.isEmpty() ? null : this.columns.get(_index);
    }

    /**
     * This is the setter method for the instance variable {@link #label}.
     *
     * @param _label the label to set
     */
    private void setLabel(final Object _label)
    {
        if (_label != null) {
            this.label = String.valueOf(_label);
        }
    }

    /**
     * This is the getter method for the instance variable {@link #columns}.
     *
     * @return value of instance variable {@link #columns}
     */
    public List<AbstractUIField> getColumns()
    {
        return this.columns;
    }

    /**
     * Setter method for instance variable {@link #browserFieldName}.
     *
     * @param _browserFieldName value for instance variable {@link #browserFieldName}
     */

    protected void setBrowserFieldName(final String _browserFieldName)
    {
        this.browserFieldName = _browserFieldName;
    }

    /**
     * Setter method for instance variable {@link #browserFieldIndex}.
     *
     * @param _browserFieldIndex value for instance variable {@link #browserFieldIndex}
     */

    protected void setBrowserFieldIndex(final int _browserFieldIndex)
    {
        this.browserFieldIndex = _browserFieldIndex;
    }

    /**
     * Getter method for the instance variable {@link #browserFieldIndex}.
     *
     * @return value of instance variable {@link #browserFieldIndex}
     */
    public int getBrowserFieldIndex()
    {
        return this.browserFieldIndex;
    }

    /**
     * This is the getter method for the instance variable
     * {@link #browserFieldName}.
     *
     * @return value of instance variable {@link #browserFieldName}
     */
    public String getBrowserFieldName()
    {
        return this.browserFieldName;
    }

    /**
     * This is the getter method for the instance variable {@link #image}.
     *
     * @return value of instance variable {@link #image}
     */

    public String getImage()
    {
        return this.image;
    }

    /**
     * Checks if is browser field.
     *
     * @param _uiField the ui field
     * @return true, if is browser field
     * @throws EFapsException on error
     */
    public boolean isBrowserField(final AbstractUIField _uiField)
        throws EFapsException
    {
        return _uiField.getFieldConfiguration().getName().equals(getBrowserFieldName());
    }

    /**
     * This is the setter method for the instance variable {@link #image}.
     *
     * @param _url the url of the image to set
     */
    private void setImage(final String _url)
    {
        if (_url != null) {
            this.image = _url;
        }
    }

    /**
     * This is the getter method for the instance variable {@link #direction}.
     *
     * @return value of instance variable {@link #direction}
     */
    public Boolean getDirection()
    {
        return this.direction;
    }

    /**
     * This is the setter method for the instance variable {@link #direction}.
     *
     * @param _direction the direction to set
     */
    public void setDirection(final Boolean _direction)
    {
        this.direction = _direction;
    }

    /**
     * Getter method for instance variable {@link #executionStatus}.
     *
     * @return value of instance variable {@link #executionStatus}
     */
    public ExecutionStatus getExecutionStatus()
    {
        return this.executionStatus;
    }

    /**
     * This method is updating the Label, by querying the eFaps-DataBase.
     */
    public void requeryLabel()
    {
        try {
            final ValueParser parser = new ValueParser(new StringReader(this.valueLabel));
            final ValueList valList = parser.ExpressionString();
            final PrintQuery print = new PrintQuery(getInstance());
            valList.makeSelect(print);
            if (print.execute()) {
                setLabel(valList.makeString(getInstance(), print, getMode()).toString());
            }
        } catch (final EFapsException e) {
            throw new RestartResponseException(new ErrorPage(e));
        } catch (final ParseException e) {
            throw new RestartResponseException(new ErrorPage(e));
        }
    }

    /**
     * Getter method for instance variable {@link #label}.
     *
     * @return value of instance variable {@link #label}
     */
    public String getLabel()
    {
        return this.label;
    }

    /**
     * Setter method for instance variable {@link #sortDirection} and for all
     * children also.
     *
     * @param _sortDirection value for instance variable {@link #sortDirection}
     */
    @Override
    public void setSortDirection(final SortDirection _sortDirection)
    {
        super.setSortDirection(_sortDirection);
        for (final UIStructurBrowser child : this.children) {
            child.setSortDirectionInternal(_sortDirection);
        }
    }

    /**
     * Getter method for instance variable {@link #expanded}.
     *
     * @return value of instance variable {@link #expanded}
     */
    public boolean isExpanded()
    {
        return this.expanded;
    }

    /**
     * Setter method for instance variable {@link #expanded}.
     *
     * @param _expanded value for instance variable {@link #expanded}
     */
    public void setExpanded(final boolean _expanded)
    {
        this.expanded = _expanded;
        storeInSession();
    }

    /**
     * This method generates the Key for a UserAttribute by using the UUID of
     * the Command and the given static part, so that for every StruturBrowser a
     * unique key for expand etc, is created.
     *
     * @return String with the key
     */
    public String getCacheKey()
    {
        return super.getCommandUUID() + "-" + UIStructurBrowser.USERSESSIONKEY;
    }

    /**
     * Store the Information in the Session.
     */
    @SuppressWarnings("unchecked")
    private void storeInSession()
    {
        try {
            if (!getMode().equals(TargetMode.CREATE)) {
                final Map<String, Boolean> sessMap;
                if (Context.getThreadContext().containsSessionAttribute(getCacheKey())) {
                    sessMap = (Map<String, Boolean>) Context.getThreadContext().getSessionAttribute(getCacheKey());
                } else {
                    sessMap = new HashMap<>();
                }
                sessMap.put(getInstanceKey(), isExpanded());
                Context.getThreadContext().setSessionAttribute(getCacheKey(), sessMap);
            }
        } catch (final EFapsException e) {
            UIStructurBrowser.LOG.error("Error storing Session info for StruturBrowser called by Command with UUID: {}",
                            getCommandUUID(), e);
        }
    }

    /**
     * Setter method for instance variable {@link #executionStatus}.
     *
     * @param _executionStatus value for instance variable {@link #executionStatus}
     */
    protected void setExecutionStatus(final ExecutionStatus _executionStatus)
    {
        this.executionStatus = _executionStatus;
    }

    /**
     * Get the Admin Object that contains the events that must be executed.
     *
     * @return the Admin Object that contains the events to be executed
     * @throws CacheReloadException on error
     */
    protected AbstractAdminObject getObject4Event()
        throws CacheReloadException
    {
        return this.getCommand();
    }

    /**
     * Getter method for the instance variable {@link #root}.
     *
     * @return value of instance variable {@link #root}
     */
    public boolean isRoot()
    {
        return this.root;
    }

    /**
     * Getter method for the instance variable {@link #parentBrws}.
     *
     * @return value of instance variable {@link #parentBrws}
     */
    public UIStructurBrowser getParentBrws()
    {
        return this.parentBrws;
    }

    /**
     * Setter method for instance variable {@link #parentBrws}.
     *
     * @param _parentBrws value for instance variable {@link #parentBrws}
     */
    protected void setParentBrws(final UIStructurBrowser _parentBrws)
    {
        this.parentBrws = _parentBrws;
    }

    /**
     * Getter method for the instance variable {@link #level}.
     *
     * @return value of instance variable {@link #level}
     */
    public int getLevel()
    {
        return this.level;
    }

    /**
     * Setter method for instance variable {@link #level}.
     *
     * @param _level value for instance variable {@link #level}
     */
    protected void setLevel(final int _level)
    {
        this.level = _level;
    }

    @Override
    public PagePosition getPagePosition()
    {
        final PagePosition ret;
        if (this.pagePosition == null) {
            //TODO remove
            UIStructurBrowser.LOG.error("MISSING PAGEPOSITION!!!");
            ret = PagePosition.CONTENT;
        } else {
            ret = this.pagePosition;
        }
        return ret;
    }

    /**
     * Setter method for instance variable {@link #pagePosition}.
     *
     * @param _pagePosition value for instance variable {@link #pagePosition}
     * @return the UI form
     */
    public UIStructurBrowser setPagePosition(final PagePosition _pagePosition)
    {
        this.pagePosition = _pagePosition;
        return this;
    }

    /**
     * (non-Javadoc).
     *
     * @see org.apache.wicket.model.Model#toString()
     * @return label
     */
    @Override
    public String toString()
    {
        return this.label;
    }
}


/*
 * Copyright 2003 - 2017 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.behaviors.dojo;

import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.text.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.wicket.Component;
import org.apache.wicket.IRequestListener;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Page;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.page.XmlPartialPageUpdate;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.request.ILogData;
import org.apache.wicket.request.IRequestCycle;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.resource.CoreLibrariesContributor;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.string.Strings;
import org.efaps.api.ci.UIFormFieldProperty;
import org.efaps.api.ui.IOption;
import org.efaps.ui.wicket.behaviors.AjaxFieldUpdateBehavior;
import org.efaps.ui.wicket.behaviors.SetSelectedRowBehavior;
import org.efaps.ui.wicket.models.field.AutoCompleteSettings;
import org.efaps.ui.wicket.resources.AbstractEFapsHeaderItem;
import org.efaps.ui.wicket.resources.EFapsContentReference;
import org.efaps.ui.wicket.util.EFapsKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.openjson.JSONArray;
import com.github.openjson.JSONException;
import com.github.openjson.JSONObject;

/**
 * TODO comment!
 *
 * @author The eFaps Team
 */
public class AutoCompleteBehavior
    extends AbstractDojoBehavior
    implements IRequestListener
{
    /**
     * Reference to the stylesheet.
     */
    public static final EFapsContentReference CSS = new EFapsContentReference(AutoCompleteBehavior.class,
                    "AutoComplete.css");

    /**
     * Logger for this class.
     */
    private static final Logger LOG = LoggerFactory.getLogger(AutoCompleteBehavior.class);

    /**
     *
     */
    private static final long serialVersionUID = 1L;

    /** the component that this handler is bound to. */
    private Component component;

    /**
     * Settings for this AutoComplete.
     */
    private final AutoCompleteSettings settings;

    /**
     * The related fieldUpdate behavior.
     */
    private AjaxFieldUpdateBehavior fieldUpdate;

    /**
     * @param _settings settings for this behavior
     */
    public AutoCompleteBehavior(final AutoCompleteSettings _settings)
    {
        if (_settings == null) {
            this.settings = new AutoCompleteSettings();
        } else {
            this.settings = _settings;
        }
    }

    /**
     * Bind this handler to the given component.
     *
     * @param _hostComponent the component to bind to
     */
    @Override
    public final void bind(final Component _hostComponent)
    {
        Args.notNull(_hostComponent, "hostComponent");

        if (this.component != null) {
            throw new IllegalStateException("this kind of handler cannot be attached to "
                            + "multiple components; it is already attached to component " + this.component
                            + ", but component " + _hostComponent + " wants to be attached too");
        }
        this.component = _hostComponent;
        this.component.setOutputMarkupId(true);
    }

    @Override
    public final void unbind(final Component _component)
    {
        this.component = null;
        super.unbind(_component);
    }

    /**
     * Gets the component that this handler is bound to.
     *
     * @return the component that this handler is bound to
     */
    protected final Component getComponent()
    {
        return this.component;
    }

    @Override
    public void renderHead(final Component _component,
                           final IHeaderResponse _response)
    {
        super.renderHead(_component, _response);
        // add wicket ajax to be sure that is is included
        CoreLibrariesContributor.contributeAjax(this.component.getApplication(), _response);

        final CharSequence ajaxBaseUrl = Strings.escapeMarkup(this.component.getRequestCycle().getUrlRenderer()
                        .getBaseUrl().toString());
        _response.render(JavaScriptHeaderItem.forScript("Wicket.Ajax.baseUrl=\"" + ajaxBaseUrl
                        + "\";", "wicket-ajax-base-url"));

        _response.render(AbstractEFapsHeaderItem.forCss(AutoCompleteBehavior.CSS));

        final String comboBoxId = "cb" + _component.getMarkupId();
        final StringBuilder js = new StringBuilder()
            .append("var ").append(comboBoxId);

        switch (this.settings.getAutoType()) {
            case SUGGESTION:
                js.append(" = new AutoSuggestion({");
                break;
            case TOKEN:
                js.append(" = new AutoTokenInput({");
                break;
            default:
                js.append(" = new AutoComplete({");
                break;
        }

        js.append("id:\"").append(_component.getMarkupId()).append("\",")
            .append("name:\"").append(this.settings.getFieldName()).append("\",")
            .append("placeHolder:ph,")
            .append("store: as,")
            .append("value: \"\",")
            .append("callbackUrl:\"" + getCallbackUrl() + "\",");

        switch (this.settings.getAutoType()) {
            case TOKEN:
                break;
            default:
                final String id = ((AutoCompleteField) _component).getItemValue();
                final String label = ((AutoCompleteField) _component).getItemLabel();
                // only if both value are valid it makes sence to add it
                if (StringUtils.isNotEmpty(id) && StringUtils.isNotEmpty(label)) {
                    js.append("item: { id:\"").append(id).append("\", name:\"").append(label)
                        .append("\", label:\"").append(label) .append("\"},");
                }
                break;
        }

        if (this.settings.getFieldConfiguration().hasProperty(UIFormFieldProperty.WIDTH)
                        && !this.settings.getFieldConfiguration().isTableField()) {
            js.append("style:\"width:").append(this.settings.getFieldConfiguration().getWidth()).append("\",");
        }
        if (!this.settings.isHasDownArrow()) {
            js.append("hasDownArrow:").append(this.settings.isHasDownArrow()).append(",");
        }
        if (this.settings.getMinInputLength() > 1) {
            js.append("minInputLength:").append(this.settings.getMinInputLength()).append(",");
        }
        if (this.settings.getSearchDelay() != 500) {
            js.append("searchDelay:").append(this.settings.getSearchDelay()).append(",");
        }

        if (!"p".equals(this.settings.getParamName())) {
            js.append("paramName:\"").append(this.settings.getParamName()).append("\",");
        }

        if (!this.settings.getExtraParameters().isEmpty()) {
            js.append("extraParameters:[");
            boolean first = true;
            for (final String ep : this.settings.getExtraParameters()) {
                if (first) {
                    first = false;
                } else {
                    js.append(",");
                }
                js.append("\"").append(ep).append("\"");
            }
            js.append("],");
        }
        if (Type.SUGGESTION.equals(this.settings.getAutoType())) {
            js.append("labelAttr: \"label\",");
        }

        js.append("searchAttr: \"name\"}, \"").append(_component.getMarkupId()).append("\");\n");

        if (this.settings.isRequired() && !Type.TOKEN.equals(this.settings.getAutoType())) {
            js.append("on(").append(comboBoxId).append(", 'change', function() {")
                 .append("var label=").append(comboBoxId).append(".item.label;")
                 .append("if (!(label === undefined || label === null)) {")
                 .append(comboBoxId).append(".item.name=label;")
                 .append(comboBoxId).append(".set(\"item\",").append(comboBoxId).append(".item);")
                 .append("}");
            if (this.fieldUpdate != null) {
                js.append(this.fieldUpdate.getCallbackScript4Dojo());
            }
            js.append("});\n");
        } else if (this.fieldUpdate != null) {
            js.append("on(").append(comboBoxId).append(", 'change', function() {")
                .append(this.fieldUpdate.getCallbackScript4Dojo())
                .append("});\n");
        }

        if (!_component.getBehaviors(SetSelectedRowBehavior.class).isEmpty()) {
            js.append("on(").append(comboBoxId).append(", 'focus', function() {")
                .append(_component.getBehaviors(
                                SetSelectedRowBehavior.class).get(0).getJavaScript("this.valueNode"))
                .append("});\n");
        }
        if (Type.TOKEN.equals(this.settings.getAutoType())) {
            final List<IOption> tokens = ((AutoCompleteField) _component).getTokens();
            for (final IOption token : tokens) {
                js.append(comboBoxId).append(".addToken(\"")
                    .append(StringEscapeUtils.escapeEcmaScript(token.getValue().toString())).append("\",\"")
                    .append(StringEscapeUtils.escapeEcmaScript(token.getLabel())).append("\");\n");
            }
        }
        _response.render(AutoCompleteHeaderItem.forScript(js.toString(), EnumSet.of(this.settings.getAutoType())));
    }

    @Override
    public void onRequest()
    {
        final WebApplication app = (WebApplication) getComponent().getApplication();
        final ACAjaxRequestTarget target = new ACAjaxRequestTarget(app.newAjaxRequestTarget(getComponent().getPage()));

        final RequestCycle requestCycle = RequestCycle.get();
        requestCycle.scheduleRequestHandlerAfterCurrent(target);

        final String val = requestCycle.getRequest()
                        .getRequestParameters()
                        .getParameterValue(this.settings.getParamName())
                        .toOptionalString();
        respond(target, val);
    }

    /**
     * @param _target   target
     * @param _val      value
     */
    protected void respond(final ACAjaxRequestTarget _target,
                           final String _val)
    {
        final AutoCompleteField field = (AutoCompleteField) getComponent();

        final Iterator<Map<String, String>> choices = field.getChoices(_val);

        try {
            final JSONArray jsonArray = new JSONArray();
            int i = 0;
            while (choices.hasNext() && (i < this.settings.getMaxResult() || this.settings.getMaxResult() == -1)) {
                final Map<String, String> map = choices.next();
                final String key = map.get(EFapsKey.AUTOCOMPLETE_KEY.getKey()) != null
                                ? map.get(EFapsKey.AUTOCOMPLETE_KEY.getKey())
                                : map.get(EFapsKey.AUTOCOMPLETE_VALUE.getKey());
                final String choice = map.get(EFapsKey.AUTOCOMPLETE_CHOICE.getKey()) != null
                                ? map.get(EFapsKey.AUTOCOMPLETE_CHOICE.getKey())
                                : map.get(EFapsKey.AUTOCOMPLETE_VALUE.getKey());
                final String value = map.get(EFapsKey.AUTOCOMPLETE_VALUE.getKey());

                final JSONObject object = new JSONObject();
                if (Type.SUGGESTION.equals(this.settings.getAutoType())) {
                    if (this.settings.getMaxChoiceLength() > 0 && choice.length() > this.settings
                                    .getMaxChoiceLength()) {
                        object.put("label", StringUtils.left(choice, this.settings.getMaxChoiceLength()) + "...");
                    } else {
                        object.put("label", choice);
                    }
                    if (this.settings.getMaxValueLength() > 0 && value.length() > this.settings.getMaxValueLength()) {
                        object.put("name", StringUtils.left(value, this.settings.getMaxChoiceLength()) + "...");
                    } else {
                        object.put("name", value);
                    }
                } else {
                    object.put("id", key);
                    if (this.settings.getMaxChoiceLength() > 0 && choice.length() > this.settings
                                    .getMaxChoiceLength()) {
                        object.put("name", StringUtils.left(choice, this.settings.getMaxChoiceLength()) + "...");
                    } else {
                        object.put("name", choice);
                    }
                    if (!choice.equals(value)) {
                        if (this.settings.getMaxValueLength() > 0 && value.length() > this.settings
                                        .getMaxValueLength()) {
                            object.put("label", StringUtils.left(value, this.settings.getMaxChoiceLength()) + "...");
                        } else {
                            object.put("label", value);
                        }
                    }
                }
                jsonArray.put(object);
                i++;
            }
            if (!(i < this.settings.getMaxResult() || this.settings.getMaxResult() == -1)) {
                final JSONObject object = new JSONObject();
                object.put("id", "MAXRESULT");
                object.put("name", "...");
                object.put("label", "...");
                jsonArray.put(object);
            }
            _target.appendJSON(jsonArray.toString());
        } catch (final JSONException e) {
            AutoCompleteBehavior.LOG.error("Catched JSONException", e);
        }
    }

    /**
     * Gets the url that references this handler.
     *
     * @return the url that references this handler
     */
    public CharSequence getCallbackUrl()
    {
        if (getComponent() == null) {
            throw new IllegalArgumentException("Behavior must be bound to a component to create the URL");
        }
        return getComponent().urlForListener(this, new PageParameters());
    }

    /**
     * @param _fieldUpdate fieldUpdate to add
     */
    public void addFieldUpdate(final AjaxFieldUpdateBehavior _fieldUpdate)
    {
        this.fieldUpdate = _fieldUpdate;
    }

    /**
     * Getter method for the instance variable {@link #settings}.
     *
     * @return value of instance variable {@link #settings}
     */
    public AutoCompleteSettings getSettings()
    {
        return this.settings;
    }

    /**
     * Config types.
     */
    public enum Type
    {
        /** Render AutomComplete.*/
        COMPLETE,
        /** Render AutoSuggestion. */
        SUGGESTION,
        /** Render AutoTokenInput. */
        TOKEN;
    }

    /**
     * Interface the component using the behavior must implement.
     */
    public interface AutoCompleteField
    {
        /**
         * @param _input the choices
         * @return new Iteratro
         */
        Iterator<Map<String, String>> getChoices(String _input);

        /**
         * @return the value for the current item
         */
        String getItemValue();

        /**
         * @return the lable for the current item
         */
        String getItemLabel();

        /**
         * @return list of tokens
         */
        List<IOption> getTokens();
    }

    /**
     * Request target wrapping the orginal target.
     */
    public static class ACAjaxRequestTarget
        implements AjaxRequestTarget
    {

        /**
         * Original target.
         */
        private final AjaxRequestTarget target;

        /**
         * The added json string.
         */
        private String jsonString;

        /**
         * @param _target target to be wrapped
         */
        public ACAjaxRequestTarget(final AjaxRequestTarget _target)
        {
            this.target = _target;
        }

        /**
         * @param _json json to be appended
         */
        public void appendJSON(final String _json)
        {
            this.jsonString = _json;
        }

        @Override
        public Integer getPageId()
        {
            return this.target.getPageId();
        }

        @Override
        public boolean isPageInstanceCreated()
        {
            return this.target.isPageInstanceCreated();
        }

        @Override
        public Integer getRenderCount()
        {
            return this.target.getRenderCount();
        }

        @Override
        public Class<? extends IRequestablePage> getPageClass()
        {
            return this.target.getPageClass();
        }

        @Override
        public PageParameters getPageParameters()
        {
            return this.target.getPageParameters();
        }

        @Override
        public void respond(final IRequestCycle _requestCycle)
        {
            final WebResponse response = (WebResponse) _requestCycle.getResponse();

            this.target.respond(_requestCycle);
            response.write("<json><![CDATA[");
            response.write(this.jsonString);
            response.write("]]></json>");
            response.write(XmlPartialPageUpdate.END_ROOT_ELEMENT);
        }

        @Override
        public void detach(final IRequestCycle _requestCycle)
        {
            this.target.detach(_requestCycle);

        }

        @Override
        public ILogData getLogData()
        {

            return this.target.getLogData();
        }

        @Override
        public void add(final Component _component,
                        final String _markupId)
        {
            this.target.add(_component, _markupId);

        }

        @Override
        public void add(final Component... _components)
        {
            this.target.add(_components);
        }

        @Override
        public void addChildren(final MarkupContainer _parent,
                                final Class<?> _childCriteria)
        {
            this.target.addChildren(_parent, _childCriteria);

        }

        @Override
        public void addListener(final IListener _listener)
        {
            this.target.addListener(_listener);

        }

        @Override
        public void appendJavaScript(final CharSequence _javascript)
        {
            this.target.appendJavaScript(_javascript);
        }

        @Override
        public void prependJavaScript(final CharSequence _javascript)
        {
            this.target.prependJavaScript(_javascript);

        }

        @Override
        public void registerRespondListener(final ITargetRespondListener _listener)
        {
            this.target.registerRespondListener(_listener);

        }

        @Override
        public Collection<? extends Component> getComponents()
        {
            return this.target.getComponents();
        }

        @Override
        public void focusComponent(final Component _component)
        {
            this.target.focusComponent(_component);
        }

        @Override
        public IHeaderResponse getHeaderResponse()
        {
            return this.target.getHeaderResponse();
        }

        @Override
        public String getLastFocusedElementId()
        {
            return this.target.getLastFocusedElementId();
        }

        @Override
        public Page getPage()
        {
            return this.target.getPage();
        }
    }
}


/*
 * Copyright 2003 - 2016 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.components.search;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;

import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.core.request.handler.PageProvider;
import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.HeadersToolbar;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.GenericPanel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.efaps.admin.dbproperty.DBProperties;
import org.efaps.admin.index.SearchConfig;
import org.efaps.admin.ui.Menu;
import org.efaps.db.Instance;
import org.efaps.json.index.result.Element;
import org.efaps.ui.wicket.pages.contentcontainer.ContentContainerPage;
import org.efaps.ui.wicket.pages.error.ErrorPage;
import org.efaps.ui.wicket.pages.main.MainPage;
import org.efaps.ui.wicket.util.DojoClasses;
import org.efaps.ui.wicket.util.DojoWrapper;
import org.efaps.util.EFapsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class ResultPanel.
 *
 * @author The eFaps Team
 */
public class ResultPanel
    extends GenericPanel<IndexSearch>
{

    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = 1L;

    /**
     * Logging instance used in this class.
     */
    private static final Logger LOG = LoggerFactory.getLogger(ResultPanel.class);

    /**
     * Instantiates a new result panel.
     *
     * @param _wicketId the wicket id
     * @param _model the model
     */
    public ResultPanel(final String _wicketId,
                       final Model<IndexSearch> _model)
    {
        super(_wicketId, _model);
        add(new Label("noResult", Model.of("")));
        add(new Label("hits", Model.of("")));
        add(new DimensionPanel("dimension", _model));
        add(getDataTable(_model.getObject()));
    }

    /**
     * Gets the data table.
     *
     * @param _indexSearch the index search
     * @return the data table
     */
    private DataTable<Element, Void> getDataTable(final IndexSearch _indexSearch)
    {
        final List<IColumn<Element, Void>> columns = new ArrayList<>();

        columns.add(new AbstractColumn<Element, Void>(new Model<>(""))
        {

            private static final long serialVersionUID = 1L;

            @Override
            public void populateItem(final Item<ICellPopulator<Element>> _cellItem,
                                     final String _componentId,
                                     final IModel<Element> _rowModel)
            {
                _cellItem.add(new Link(_componentId, _rowModel));
            }
        });

        if (_indexSearch.getSearch() == null || _indexSearch.getSearch().getResultFields().isEmpty()) {
            columns.add(new PropertyColumn<Element, Void>(new Model<>(""), "text"));
        } else {
            for (final Entry<String, Collection<String>> entry : _indexSearch.getSearch().getResultFields()
                            .entrySet()) {
                columns.add(new ResultColumn(_indexSearch.getSearch().getResultLabel().get(entry.getKey()), entry
                                .getValue()));
            }
        }
        final DataTable<Element, Void> ret = new DataTable<>("table", columns, _indexSearch
                        .getDataProvider(), _indexSearch.getSearch() == null ? 100
                                        : _indexSearch.getSearch().getNumHits());

        ret.addTopToolbar(new HeadersToolbar<>(ret, null));
        return ret;
    }

    /**
     * Update.
     *
     * @param _target the target
     * @param _indexSearch the index search
     */
    public void update(final AjaxRequestTarget _target,
                       final IndexSearch _indexSearch)
    {

        ResultPanel.this.visitChildren(DimensionPanel.class, new IVisitor<Component, Void>()
        {
            @Override
            public void component(final Component _component,
                                  final IVisit<Void> _visit)
            {
                try {
                    if (_indexSearch.getSearch().getConfigs().contains(SearchConfig.ACTIVATE_DIMENSION)) {
                        _component.setVisible(true);
                    } else {
                        _component.setVisible(false);
                    }
                } catch (final EFapsException e) {
                    ResultPanel.LOG.error("Catched error", e);
                }
                _visit.dontGoDeeper();
            }
        });

        ResultPanel.this.visitChildren(DataTable.class, new IVisitor<Component, Void>()
        {
            @Override
            public void component(final Component _component,
                                  final IVisit<Void> _visit)
            {
                if (_indexSearch.getResult().getElements().isEmpty()) {
                    _component.setVisible(false);
                } else {
                    _component.setVisible(true);
                }
                if (_indexSearch.isUpdateTable()) {
                    _component.replaceWith(getDataTable(_indexSearch));
                }
                _visit.dontGoDeeper();
            }
        });

        ResultPanel.this.visitChildren(Label.class, new IVisitor<Component, Void>()
        {

            @Override
            public void component(final Component _component,
                                  final IVisit<Void> _visit)
            {
                final String compid = _component.getId();
                switch (compid) {
                    case "hits":
                        if (_indexSearch.getResult().getElements().isEmpty()) {
                            _component.setVisible(false);
                        } else {
                            _component.setVisible(true);
                            ((Label) _component).setDefaultModelObject(DBProperties.getFormatedDBProperty(
                                            ResultPanel.class.getName() + ".Hits", _indexSearch.getResult()
                                                            .getElements().size(), _indexSearch.getResult()
                                                                            .getHitCount()));
                        }
                        break;
                    case "noResult":
                        if (_indexSearch.getResult().getElements().isEmpty()) {
                            _component.setVisible(true);
                            ((Label) _component).setDefaultModelObject(DBProperties.getProperty(ResultPanel.class
                                            .getName() + ".NoResult"));
                        } else {
                            _component.setVisible(false);
                        }
                        break;
                    default:
                        break;
                }
            }
        });
    }

    /**
     * The Class Link.
     *
     * @author The eFaps Team
     */
    public static class Link
        extends AjaxLink<Element>
    {

        /** The Constant serialVersionUID. */
        private static final long serialVersionUID = 1L;

        /**
         * Instantiates a new link.
         *
         * @param _wicketId the wicket id
         * @param _model the model
         */
        public Link(final String _wicketId,
                    final IModel<Element> _model)
        {
            super(_wicketId, _model);
            setBody(Model.of(""));
            add(new AttributeAppender("class", "eFapsLink", " "));
        }

        @Override
        @SuppressWarnings("checkstyle:illegalcatch")
        public void onClick(final AjaxRequestTarget _target)
        {
            final ResultPanel resultPanel = findParent(ResultPanel.class);
            final Element element = (Element) getDefaultModelObject();
            final Menu menu;
            final Instance instance;
            try {
                instance = Instance.get(element.getOid());
                menu = Menu.getTypeTreeMenu(instance.getType());
            } catch (final Exception e) {
                throw new RestartResponseException(new ErrorPage(e));
            }
            if (menu == null) {
                final Exception ex = new Exception("no tree menu defined for type " + instance.getType().getName());
                throw new RestartResponseException(new ErrorPage(ex));
            }
            Page page;
            try {
                page = new ContentContainerPage(menu.getUUID(), instance.getOid());
            } catch (final EFapsException e) {
                page = new ErrorPage(e);
            }

            final CharSequence pageUrl;
            final RequestCycle requestCycle = RequestCycle.get();

            page.getSession().getPageManager().touchPage(page);
            if (page.isPageStateless()) {
                pageUrl = requestCycle.urlFor(page.getClass(), page.getPageParameters());
            } else {
                final IRequestHandler handler = new RenderPageRequestHandler(new PageProvider(page));
                pageUrl = requestCycle.urlFor(handler);
            }

            final StringBuilder js = new StringBuilder();
            js.append("var rN = dom.byId('").append(resultPanel.getMarkupId()).append("');\n")
                .append("var dialog = registry.byId(rN.id);\n")
                .append("popup.close(dialog);\n")
                .append("registry.byId(\"").append("mainPanel")
                .append("\").set(\"content\", domConstruct.create(\"iframe\", {")
                .append("\"id\": \"").append(MainPage.IFRAME_ID)
                .append("\",\"src\": \"").append(pageUrl)
                .append("\",\"style\": \"border: 0; width: 100%; height: 99%\"")
                .append(",\"id\": \"").append(MainPage.IFRAME_ID).append("\"")
                .append("}));");
            _target.appendJavaScript(DojoWrapper.require(js, DojoClasses.TooltipDialog, DojoClasses.popup,
                            DojoClasses.dom, DojoClasses.domConstruct, DojoClasses.registry));
        }
    }

    /**
     * The Class ResultColumn.
     */
    public static class ResultColumn
        extends AbstractColumn<Element, Void>
    {

        /**
         *
         */
        private static final long serialVersionUID = 1L;

        /** The keys. */
        private final Collection<String> keys;

        /**
         * Instantiates a new result column.
         *
         * @param _label the label
         * @param _keys the keys
         */
        public ResultColumn(final String _label,
                            final Collection<String> _keys)
        {
            super(Model.of(_label));
            this.keys = _keys;
        }

        @Override
        public void populateItem(final Item<ICellPopulator<Element>> _cellItem,
                                 final String _componentId,
                                 final IModel<Element> _rowModel)
        {
            String val = "";
            for (final String key : this.keys) {
                if (_rowModel.getObject().getFields().containsKey(key)) {
                    val = _rowModel.getObject().getFields().get(key);
                    break;
                }
            }
            _cellItem.add(new Label(_componentId, val));
        }
    }
}


/*
 * Copyright 2003 - 2017 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.behaviors.dojo;

import org.apache.wicket.Component;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.efaps.ui.wicket.util.DojoClasses;
import org.efaps.ui.wicket.util.DojoWrapper;

/**
 * TODO comment!
 *
 * @author The eFaps Team
 */
public class LazyIframeBehavior
    extends AbstractDojoBehavior
{

    /**
     * Needed for serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Markup id for the generated iframe.
     */
    private String frameMarkupId;

    /**
     * @param _frameMarkupId markup id for the generated iframe
     */
    public LazyIframeBehavior(final String _frameMarkupId)
    {
        this.frameMarkupId = _frameMarkupId;
    }

    /**
     * Getter method for the instance variable {@link #frameMarkupId}.
     *
     * @return value of instance variable {@link #frameMarkupId}
     */
    public String getFrameMarkupId()
    {
        return this.frameMarkupId;
    }

    /**
     * Setter method for instance variable {@link #frameMarkupId}.
     *
     * @param _frameMarkupId value for instance variable {@link #frameMarkupId}
     */
    public void setFrameMarkupId(final String _frameMarkupId)
    {
        this.frameMarkupId = _frameMarkupId;
    }

    /**
     * Render the links for the head.
     *
     * @param _component component the header will be rendered for
     * @param _response resonse to add
     */
    @Override
    public void renderHead(final Component _component,
                           final IHeaderResponse _response)
    {
        super.renderHead(_component, _response);
        final StringBuilder js = new StringBuilder()
            .append("ready(function() {")
            .append("registry.byId(\"").append(_component.getMarkupId())
            .append("\").set(\"content\", domConstruct.create(\"iframe\", {");

        if (this.frameMarkupId != null) {
            js.append("\"id\": \"").append(this.frameMarkupId).append("\",");
        }

        js.append("\"src\": \"").append(_component.urlForListener(new PageParameters()))
            .append("\",\"style\": \"border: 0; width: 100%; height: 99%\", \"nodeId\": \"jan\"")
            .append("}));")
            .append("});");
        _response.render(JavaScriptHeaderItem.forScript(DojoWrapper.require(js, DojoClasses.ready,
                        DojoClasses.registry, DojoClasses.domConstruct), _component.getMarkupId() + "-Script"));
    }
}


/*
 * Copyright 2003 - 2016 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.efaps.ui.wicket.models.field.factories;

import org.apache.wicket.Component;
import org.apache.wicket.model.Model;
import org.efaps.admin.datamodel.ui.NumberUI;
import org.efaps.ui.wicket.components.values.NumberField;
import org.efaps.ui.wicket.models.field.AbstractUIField;
import org.efaps.ui.wicket.models.field.validators.FormNumberValidator;
import org.efaps.util.EFapsException;

/**
 * TODO comment!
 *
 * @author The eFaps Team
 */
@SuppressWarnings("checkstyle:abstractclassname")
public final class NumberUIFactory
    extends StringUIFactory
{
    /**
     * Factory Instance.
     */
    private static NumberUIFactory FACTORY;

    /**
     * Singelton.
     */
    private NumberUIFactory()
    {
    }

    @Override
    public Component getEditable(final String _wicketId,
                                 final AbstractUIField _uiField)
        throws EFapsException
    {
        Component ret = null;
        if (applies(_uiField)) {
            switch (_uiField.getFieldConfiguration().getUIType()) {
                case DEFAULT:
                case NUMBER:
                    ret = new NumberField(_wicketId, Model.of(_uiField), _uiField.getFieldConfiguration());
                    break;
                case BUTTON:
                case CHECKBOX:
                case DROPDOWN:
                case RADIO:
                case SNIPPLET:
                    ret = ((UITypeFactory) UITypeFactory.get()).getEditableComp(_wicketId, _uiField);
                    ret.add(new FormNumberValidator(_uiField));
                    break;
                default:
                    break;
            }
        }
        return ret;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Component getHidden(final String _wicketId,
                               final AbstractUIField _abstractUIField)
        throws EFapsException
    {
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean applies(final AbstractUIField _abstractUIField)
        throws EFapsException
    {
        return _abstractUIField.getValue().getUIProvider() instanceof NumberUI;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String getReadOnlyValue(final AbstractUIField _abstractUIField)
        throws EFapsException
    {
        String strValue = "";
        final Object valueTmp = _abstractUIField.getValue()
                        .getReadOnlyValue(_abstractUIField.getParent().getMode());
        if (valueTmp instanceof Number) {
            strValue = String.valueOf(valueTmp);
        } else {
            strValue = valueTmp == null ? "" : String.valueOf(valueTmp);
        }
        return strValue;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getKey()
    {
        return NumberUIFactory.class.getName();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getPickListValue(final AbstractUIField _uiField)
        throws EFapsException
    {
        return getReadOnlyValue(_uiField);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Comparable<?> getCompareValue(final AbstractUIField _uiField)
        throws EFapsException
    {
        return (Comparable<?>) _uiField.getValue().getReadOnlyValue(_uiField.getParent().getMode());
    }

    /**
     * @return IComponentFactory instance
     */
    public static IComponentFactory get()
    {
        if (NumberUIFactory.FACTORY == null) {
            NumberUIFactory.FACTORY = new NumberUIFactory();
        }
        return NumberUIFactory.FACTORY;
    }
}


/*
 * Copyright 2003 - 2014 The eFaps Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Revision:        $Rev$
 * Last Changed:    $Date$
 * Last Changed By: $Author$
 */

package org.efaps.ui.wicket.components.modalwindow;

import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.model.IModel;
import org.efaps.admin.dbproperty.DBProperties;
import org.efaps.ui.wicket.util.Configuration;
import org.efaps.ui.wicket.util.Configuration.ConfigAttribute;

/**
 * Abstract Modal window to set the stylesheets equaly for all modal in eFaps.
 *
 * @author The eFaps Team
 * @version $Id$
 */
public abstract class AbstractModalWindow
    extends ModalWindow
{
    /**
     * Needed for serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * @param _wicketId wicket id for this component
     */
    public AbstractModalWindow(final String _wicketId)
    {
        super(_wicketId);
        initialize();
    }

    /**
     * @param _wicketId wicket id for this component
     * @param _model model for this component
     */
    public AbstractModalWindow(final String _wicketId,
                               final IModel<?> _model)
    {
        super(_wicketId, _model);
        initialize();
    }

    /**
     * Initialize.
     */
    protected void initialize()
    {
        if ("w_silver".equals(Configuration.getAttribute(ConfigAttribute.DOJO_MODALCLASS))) {
            setCssClassName(ModalWindow.CSS_CLASS_GRAY);
        } else if ("w_blue".equals(Configuration.getAttribute(ConfigAttribute.DOJO_MODALCLASS))) {
            setCssClassName(ModalWindow.CSS_CLASS_BLUE);
        }
        showUnloadConfirmation(false);
        setTitle(DBProperties.getProperty("Logo.Version.Label"));
    }
}