gdt.data.store.Entigrator.java Source code

Java tutorial

Introduction

Here is the source code for gdt.data.store.Entigrator.java

Source

package gdt.data.store;

/*
 * Copyright 2016 Alexander Imas
 * This file is part of JEntigrator.
    
JEntigrator is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
JEntigrator is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with JEntigrator.  If not, see <http://www.gnu.org/licenses/>.
 */
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
//import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Stack;
import java.util.logging.Logger;

import org.apache.commons.codec.binary.Base64;

import gdt.data.grain.Core;
import gdt.data.grain.Identity;
import gdt.data.grain.Sack;
import gdt.data.grain.Support;
import gdt.data.store.FileExpert;

/**
* This class is the main provider of database management methods.
* @author  Alexander Imas
* @version 1.0
* @since   2016-03-11
*/

public class Entigrator {
    /**
     * The name of headers directory. This directory contains headers
     * for all entities within the database. A header provides the short
     * information about an entity. The usage of headers accelerates 
     * building entity lists.    
     */
    public static final String HEADERS = "_AM7SyUTiAcrd_hDOtNegtzohEbc";
    /**
     * The name of the properties file. This XML file contains all property names
     * linked to property name/value maps.   
     */
    public static final String PROPERTY_INDEX = "_OMw7Msp2wcy5tMEmoayqGzwIJE8";
    /**
     * The name of the icons directory.
     */
    public static final String ICONS = "_juwhpT_txc_SIN45Mt48Tt3FIEOc";
    /**
       * The name of the quick map file. This XML file contains the shortest
       * information about entities. The usage of the quick map accelerates 
       * access to entities.    
       */
    public static final String DEPENDENCIES = "_34OGgNhynOBECkS_SlCJt_SNh_SY9Q";
    public static final String QUICK_MAP = "_0Hw7Cb9q5VrmwG6enFmb5GBKIXo";
    /**
     * Reserved attribute name. The entities cache uses time stamp to keep
     * recent entities in memory some time to accelerate the multiple
     * sequential access to the same entity.  
       */
    public static final String TIMESTAMP = "timestamp";
    /**
       * This directory contains property name/value maps.   
       */
    public static final String PROPERTY_BASE = "_mVr_SNS5qWHZiO9qPAlAOfTWrMZQ";
    /**
      * This directory contains property value/entity maps.   
      */
    public static final String PROPERTY_MAP = "_A6nLHPcMALLI8jWAj4wPUuYOJOo";
    /**
       * This directory contains entities files.   
       */
    public static final String ENTITY_BASE = "_xXsnV5R_SGxkuH_SpLJDeXCglybws";
    /**
       * Reserved attribute. Entities having PRESERVED assigned cannot be 
       * deleted.   
       */
    public static final String PRESERVED = "preserved";
    /**
       * The parent directory of the database.
       */
    public static final String ENTIHOME = "entihome";
    private final int ENT_OK = 0;
    private final int WRONG_ARG = -1;
    private final int ENT_PROP_NOT_ASSIGNED = 1;
    private final int ENT_MULTIPLE_VALUES_OK = 2;
    private final int ENT_MULTIPLE_VALUES_BAD = 3;
    private final int INDX_NO_PROP_ENTRY = 5;
    private final int INDX_NO_PROP_SACK = 6;
    private final int INDX_NO_MAP_ENTRY = 7;
    private final int INDX_NO_MAP_SACK = 8;
    private final int INDX_NO_ENTITY_ENTRY = 9;
    private final int INDX_ENTITY_FALSE_MAP = 11;
    private final int INDX_OK = 12;
    private Sack propertyIndex;
    private Sack quickMap;
    private EntitiesCache entitiesCache;
    private Properties locatorsCache;
    private Hashtable<String, Class<?>> classesCache;
    private final Logger LOGGER = Logger.getLogger(Entigrator.class.getName());
    @SuppressWarnings("unused")
    private int progress;
    private String entihome$;

    /**
     * Constructor.
     * 
     * @param arguments string array with size at least 1.
     * arguments[0] must be the path of the parent directory
     * of the database. 
     */
    public Entigrator(String[] arguments) {
        if (arguments == null) {
            LOGGER.severe(": no arguments");
            return;
        }

        entihome$ = arguments[0];
        //        System.out.println("Entigrator:constructor:entihome="+entihome$);
        try {
            File propIndex = new File(entihome$ + "/" + PROPERTY_INDEX);
            boolean badIndex = false;
            if (!propIndex.exists() || propIndex.length() < 10)

            {
                //              System.out.println("Entigrator:constructor:cannot find property index=="+propIndex.getPath());
                badIndex = true;
            } else {
                propertyIndex = Sack.parseXML(entihome$ + "/" + PROPERTY_INDEX);
                if (propertyIndex == null)
                    badIndex = true;
                else {
                    String[] sa = propertyIndex.elementListNoSorted("property");
                    if (sa == null)
                        badIndex = true;
                }
            }
            if (badIndex) {
                propertyIndex = new Sack();
                propertyIndex.putAttribute(new Core("String", "residence.base", "register"));
                propertyIndex.putAttribute(new Core("String", "alias", "property.index"));
                propertyIndex.createElement("property");
                propertyIndex.setKey(PROPERTY_INDEX);
                propertyIndex.setPath(PROPERTY_INDEX);
                propertyIndex.saveXML(getEntihome() + "/" + PROPERTY_INDEX);
                indx_rebuild(null);
            }
            if (propertyIndex == null)
                propertyIndex = Sack.parseXML(entihome$ + "/" + PROPERTY_INDEX);
            if (propertyIndex == null) {
                //System.out.println("Entigrator:constructor:cannot get property index");
                LOGGER.severe(":cannot get property index");
            }
        } catch (Exception e) {
            //System.out.println("Entigrator:constructor:cannot find property index:" + e.toString());
            LOGGER.severe(":" + e.toString());
        }

        entitiesCache = new EntitiesCache(this);
        makeQuickMap();

    }

    /**
     * Refresh an entity from cache or disk.
     *  @param sack the entity.
     * @return the entity.
     */
    public synchronized Sack get(Sack sack) {
        if (sack == null)
            return null;
        //sack.print();
        try {
            String base$ = sack.getAttributeAt("residence.base");
            String entityBase$ = ENTITY_BASE;
            if (base$.equals(entityBase$)) {
                return entitiesCache.get(sack.getKey());
            }

            String path$ = entihome$;
            if ("register".equals(base$))
                path$ = path$ + "/" + sack.getPath();
            else
                path$ = path$ + "/" + base$ + "/data/" + sack.getKey();
            //System.out.println("Entigrator:get:path="+path$);
            return Sack.parseXML(path$);
        } catch (Exception e) {
            // System.out.println("Entigrator:get:cannot find sack=" + sack.getKey());
            LOGGER.severe(":get:cannot find sack=" + sack.getKey());
            return null;
        }
    }

    /**
      * Save an entity.
      *  @param sack the entity.
      * @return true if success false otherwise.
      */
    public synchronized boolean save(Sack sack) {
        if (sack == null) {
            System.out.println("Entigrator:save:sack is null");
            return false;
        }
        // System.out.println("Entigrator:save:"+sack.getKey());
        String entityBase$ = ENTITY_BASE;
        String base$ = sack.getAttributeAt("residence.base");
        if (entityBase$.equals(base$)) {
            // System.out.println("Entigrator:save:put in cache:"+sack.getKey());
            entitiesCache.put(sack);
            // saveNative(sack);
            return true;
        } else {
            return saveNative(sack);
        }
    }

    /**
      * Store an entity on the disk.
      *  @param sack the entity.
      * @return true if success false otherwise.
      */
    public synchronized boolean saveNative(Sack sack) {
        if (sack == null) {
            // System.out.println("Entigrator:save:sack is null");
            LOGGER.severe(":saveNative:sack is null");
            return false;
        }
        //System.out.println("Entigrator:saveNative:"+sack.getKey()+" label="+sack.getProperty("label"));
        String key$ = sack.getKey();
        String base$ = sack.getAttributeAt("residence.base");
        String path$ = entihome$;
        if ("register".equals(base$))
            path$ = path$ + "/" + key$;
        else
            path$ = path$ + "/" + base$ + "/data/" + key$;
        try {
            //   System.out.println("Entigrator:save:sack="+sack.getKey()+" path="+path$);
            sack.saveXML(path$);
            notifyAll();
            String label$ = sack.getProperty("label");
            if (label$ != null) {
                quickMap.putElementItem("label",
                        new Core(sack.getAttributeAt("icon"), sack.getProperty("label"), key$));
                quickMap.putElementItem("key", new Core(label$, key$, sack.getProperty("entity")));
                Sack header = null;
                String header$ = getEntihome() + "/" + HEADERS + "/" + key$;
                File headerFile = new File(header$);
                if (headerFile.exists())
                    header = Sack.parseXML(header$);
                if (header == null) {
                    header = new Sack();
                    header.createElement("label");
                    header.createElement("key");
                    header.setKey(key$);
                    header.setPath(header$);
                }
                header.putElementItem("label",
                        new Core(sack.getAttributeAt("icon"), sack.getProperty("label"), key$));
                header.putElementItem("key", new Core(label$, key$, sack.getProperty("entity")));
                header.putAttribute(new Core(null, TIMESTAMP, String.valueOf(System.currentTimeMillis())));
                header.saveXML(header$);
                return true;
            }
        } catch (Exception e) {
            LOGGER.severe("saveNative:" + e.toString());
        }
        return false;
    }

    /**
      * Check entity file.
      *  @param entityKey$ key of the entity
      * @return true if entity file exists false otherwise.
      */
    private boolean touchEntity(String entityKey$) {
        try {
            String entihome$ = getEntihome();
            String memberPath$ = entihome$ + "/" + getBaseName() + "/data/" + entityKey$;
            File member = new File(memberPath$);
            return !(!member.exists() || member.length() < 10);
        } catch (Exception e) {
            LOGGER.severe(":touchEntity:" + e.toString());
            return false;
        }
    }

    /**
      * Get entity by key.
      *  @param entityKey$ key of the entity
      * @return entity or null.
      */
    public synchronized Sack getEntityAtKey(String entityKey$) {
        return entitiesCache.get(entityKey$);
    }

    private synchronized Sack getMember(String baseAlias$, String candidate$) {
        if ("entity.base".equals(baseAlias$))
            return entitiesCache.get(candidate$);
        String entihome$ = getEntihome();
        String base$ = null;
        if ("entity.base".equals(baseAlias$))
            base$ = ENTITY_BASE;
        if ("property.base".equals(baseAlias$))
            base$ = PROPERTY_BASE;
        if ("property.map.base".equals(baseAlias$))
            base$ = PROPERTY_MAP;
        if (base$ == null) {
            LOGGER.severe(":getMember:cannot find base alias=" + baseAlias$);
            return null;
        }
        String memberPath$ = entihome$ + "/" + base$ + "/data/" + candidate$;
        File member = new File(memberPath$);
        if (!member.exists()) {
            //   LOGGER.severe(":getMember:cannot find member at path="+memberPath$); 
            return null;
        }
        if (member.length() < 10) {
            try {
                LOGGER.severe(":getMember:delete wrong member at path=" + memberPath$);
                member.delete();
            } catch (Exception e) {
                LOGGER.severe(":getMember:" + e.toString());
            }
            return null;
        }
        try {
            return Sack.parseXML(memberPath$);
        } catch (Exception e) {
            LOGGER.severe(":getMember:" + e.toString());
            return null;
        }
    }

    /**
       * Load entity by key from disk.
       *  @param entityKey$ key of the entity
       * @return entity or null.
       */
    public synchronized Sack getEntity(String entityKey$) {
        //System.out.println("Entigrator:getMember:base="+baseAlias$+" candidate="+candidate$);
        String entihome$ = getEntihome();
        String base$ = ENTITY_BASE;
        if (base$ == null) {
            LOGGER.severe(":getEntity:cannot find entity base");
            return null;
        }
        String memberPath$ = entihome$ + "/" + base$ + "/data/" + entityKey$;
        File member = new File(memberPath$);
        if (!member.exists())
            return null;
        if (member.length() < 10) {
            try {
                LOGGER.severe(":getEntity:delete wrong entity:base=" + base$ + " entity=" + entityKey$);
                member.delete();
            } catch (Exception e) {
                LOGGER.severe(":getEntity:" + e.toString());
            }
            return null;
        }
        try {
            return Sack.parseXML(memberPath$);
        } catch (Exception e) {
            LOGGER.severe(":getEntity:" + e.toString());
            return null;
        }
    }

    /**
       * List all property names.
       * 
       * @return array of property names.
       */
    public String[] indx_listPropertyNames() {
        String[] sa = propertyIndex.elementListNoSorted("property");
        String[] sa1 = new String[sa.length + 1];
        for (int i = 0; i < sa.length; i++)
            sa1[i] = sa[i];
        sa1[sa.length] = "label";
        Support.sortStrings(sa1);
        return sa1;
    }

    /**
       * List all property values.
       *  @param propertyName$ the name of the property.
       * @return array of property values or null.
       */
    public String[] indx_listPropertyValues(String propertyName$) {
        if (propertyName$ == null) {
            LOGGER.severe(":indx_listPropertyValues:property name is null");
            return null;
        }
        if ("label".equals(propertyName$)) {
            return indx_listAllLabels();
        }
        Core core = propertyIndex.getElementItem("property", propertyName$);
        if (core == null) {
            LOGGER.severe(":indx_listPropertyValues:cannot find property =" + propertyName$);
            return null;
        }
        Sack property = getMember("property.base", core.value);
        if (property == null) {
            LOGGER.severe(":indx_listPropertyValues:cannot find property  name=" + core.value);
            return null;
        }
        return property.elementList("value");
    }

    private Sack indx_getProperty(String propertyName$) {
        Core core = propertyIndex.getElementItem("property", propertyName$);
        if (core == null) {
            LOGGER.severe(":indx_getProperty:cannot get property key for property=" + propertyName$);
            return null;
        }
        return getMember("property.base", core.value);
    }

    /**
        * Change property name.
        *  @param propertyName$ old property name
        *  @param newPropertyName$ new property name.
        */
    public void prp_editPropertyName(String propertyName$, String newPropertyName$) {
        Core core = propertyIndex.getElementItem("property", propertyName$);

        if (core == null) {
            LOGGER.severe(":indx_editProperty:cannot get property key for property=" + propertyName$);

        }
        propertyIndex.removeElementItem("property", propertyName$);
        propertyIndex.putElementItem("property", new Core(core.value, newPropertyName$, core.value));
        save(propertyIndex);
    }

    private Sack indx_getPropertyMap(String propertyName$, String propertyValue$) {
        if (propertyName$ == null) {
            LOGGER.severe(":indx_getPropertyMap:argument is null");
            return null;
        }
        Core core = propertyIndex.getElementItem("property", propertyName$);
        if (core == null) {
            String map$ = indx_addPropertyValue(propertyName$, propertyValue$);
            return getMember("property.map.base", map$);
        }
        Sack property = getMember("property.base", core.value);
        if (property == null) {
            String map$ = indx_addPropertyValue(propertyName$, propertyValue$);
            return getMember("property.map.base", map$);
        }
        String map$ = property.getElementItemAt("value", propertyValue$);
        if (map$ == null) {
            map$ = indx_addPropertyValue(propertyName$, propertyValue$);
        }
        Sack map = getMember("property.map.base", map$);
        if (map == null) {
            property.removeElementItem("value", propertyValue$);
            save(property);
            map$ = indx_addPropertyValue(propertyName$, propertyValue$);
        }
        return getMember("property.map.base", map$);
    }

    /**
     * List keys of entities having certain properties assigned.
     *  @param criteria  the properties object that contains a set
     *  of property name/value pairs.
     * @return string array of entities keys.
     */
    public String[] indx_listEntities(Properties criteria) {
        if (criteria == null)
            return null;
        Enumeration<?> en = criteria.keys();
        String propertyName$;
        String propertyValue$;
        String[] cur = null;
        String[] sum = null;
        int i = 0;
        while (en.hasMoreElements()) {
            try {
                propertyName$ = (String) en.nextElement();
                propertyValue$ = criteria.getProperty(propertyName$);
                cur = indx_listEntities(propertyName$, propertyValue$);
                if (cur == null)
                    return null;
                if (i++ == 0)
                    sum = cur;
                else
                    sum = intersect(sum, cur);
                if (sum == null || sum.length < 1)
                    return null;
            } catch (Exception e) {
                LOGGER.severe(":indx_listEntities:" + e.toString());
            }
        }
        return sum;
    }

    /**
     * Get entity key by entity label
     *  @param label$ entity label
     * @return entity key.
     */
    public String indx_keyAtLabel(String label$) {
        return quickMap.getElementItemAt("label", label$);
    }

    /**
     * Get entity label by entity key
     *  @param key$ entity key
     * @return entity label.
     */
    public String indx_getLabel(String key$) {
        try {
            String label$ = null;
            Core key = quickMap.getElementItem("key", key$);
            if (key != null && key.type != null) {
                label$ = key.type;
                return label$;
            }
            if (label$ == null) {
                Sack entity = Sack.parseXML(entihome$ + "/" + ENTITY_BASE + "/data/" + key$);
                label$ = entity.getProperty("label");
                if (label$ != null) {
                    quickMap.putElementItem("label",
                            new Core(entity.getAttributeAt("icon"), label$, entity.getKey()));
                    quickMap.putElementItem("label", new Core(entity.getProperty("entity"), key$, label$));
                    return label$;
                }
            }
        } catch (Exception e) {
            LOGGER.severe(":indx_getLabel:" + e.toString());
        }
        return null;
    }

    /**
     * Get icon file by entity key.
     *  @param key$ entity key
     * @return the name of icon file.
     */
    public String indx_getIcon(String key$) {
        try {
            Core key = quickMap.getElementItem("key", key$);
            Core label = quickMap.getElementItem("label", key.type);
            return label.type;
        } catch (Exception e) {
            LOGGER.severe(e.toString());
        }
        return null;
    }

    /**
     * Get entities labels by entities keys.
     *  @param keys array of keys
     * @return array of labels.
     */
    public String[] indx_getLabels(String[] keys) {
        ArrayList<String> sl = new ArrayList<String>();
        String label$;
        for (String aKeys : keys) {
            try {
                label$ = quickMap.getElementItem("key", aKeys).type;
                if (label$ != null)
                    sl.add(label$);
            } catch (Exception e) {
                LOGGER.severe(":indx_getLabels:" + e.toString());
            }
        }
        return sl.toArray(new String[0]);
    }

    /**
     * Get entity type by entity label.
     *  @param key$ entity key.
     * @return entity type.
     */
    public String getEntityType(String key$) {
        try {
            return quickMap.getElementItem("key", key$).value;
        } catch (Exception ee) {
            // LOGGER.info(ee.toString());
            return null;
        }
    }

    /**
     * Get entity icon file by entity key.
     *  @param key$ entity key.
     * @return entity icon file name.
     */
    public String getEntityIcon(String key$) {
        try {
            String label$ = quickMap.getElementItem("key", key$).type;
            return quickMap.getElementItem("label", label$).type;
        } catch (Exception ee) {
            //  LOGGER.info(ee.toString());
            return null;
        }
    }

    /**
     * Get entities icon files and labels by entities keys.
     *  @param keys array of entities keys.
     * @return array of cores where type is a name of icon file,
     * name is a key of the entity and value is a label of the entity.
     */
    public Core[] indx_getMarks(String[] keys) {
        try {
            ArrayList<Core> cl = new ArrayList<Core>();
            String label$;
            String icon$;
            Core key;
            for (String aKeys : keys) {
                label$ = null;
                try {
                    key = quickMap.getElementItem("key", aKeys);
                    if (key == null) {
                        LOGGER.severe(":indx_getMarks:cannot get mark at key=" + aKeys);
                        continue;
                    }
                    label$ = key.type;
                } catch (Exception ee) {
                    LOGGER.severe(":indx_getMarks:" + ee.toString());
                    continue;
                }
                icon$ = quickMap.getElementItem("label", label$).type;
                if (label$ != null)
                    cl.add(new Core(icon$, aKeys, label$));
            }
            return cl.toArray(new Core[0]);
        } catch (Exception e) {
            LOGGER.severe(":indx_getMarks:" + e.toString());
        }
        return null;
    }

    /**
     * Get entities icon files and keys by entities labels.
     *  @param labels array of entities labels.
     * @return array of cores where type is a name of icon file,
     * name is a key of the entity and value is a label of the entity.
     */
    public Core[] indx_getMarksAtLabels(String[] labels) {
        try {

            ArrayList<Core> cl = new ArrayList<Core>();
            Core label;
            for (String aLabel : labels) {
                label = quickMap.getElementItem("label", aLabel);
                if (label != null)
                    cl.add(new Core(label.type, label.value, label.name));
            }
            return cl.toArray(new Core[0]);
        } catch (Exception e) {
            LOGGER.severe(":indx_getMarksAtLabel:" + e.toString());
        }
        return null;
    }

    /**
     * Get labels of all entities.
     * @return array of labels
     */

    public String[] indx_listAllLabels() {
        try {
            String[] sa = quickMap.elementList("label");
            Support.sortStrings(sa);
            return sa;
        } catch (Exception e) {
            LOGGER.severe(":indx_listAllLabels:" + e.toString());
            return null;
        }
    }

    /**
     * Get keys of entities having certain property name/value assigned. 
     *  @param propertyName$ property name
     *  @param propertyValue$ propertyValue$.
     * @return array of entities keys.
     */
    public String[] indx_listEntities(String propertyName$, String propertyValue$) {
        if (propertyName$ == null || propertyValue$ == null) {
            //System.out.println("Entigrator:indx_listEntities:one or more null parameters");
            LOGGER.severe(":indx_listEntities:one or more null parameters");
            return null;
        }
        if ("label".equals(propertyName$)) {
            String key$ = indx_keyAtLabel(propertyValue$);
            if (key$ == null)
                return null;
            return new String[] { key$ };
        }
        if ("entity".equals(propertyName$)) {
            try {
                //   Sack labelMap=Sack.parseXML(getEntihome()+"/"+LABEL_MAP);
                ArrayList<String> sl = new ArrayList<String>();
                Core[] ca = quickMap.elementGet("key");
                for (Core aCa : ca) {
                    if (propertyValue$.equals(aCa.value))
                        sl.add(aCa.name);
                }
                return sl.toArray(new String[0]);
            } catch (Exception e) {
                LOGGER.severe(":indx_listEntities:" + e.toString());

            }
        }
        Core core = propertyIndex.getElementItem("property", propertyName$.trim());

        if (core == null) {
            LOGGER.severe(
                    ":indx_listEntities:cannot find property entry =" + propertyName$ + " value=" + propertyValue$);
            return null;

        }
        Sack property = getMember("property.base", core.value);
        if (property == null) {
            LOGGER.severe(":indx_listEntities:cannot find property=" + core.value + " found for property name="
                    + propertyName$ + " value=" + propertyValue$);
            return null;

        }
        String map$ = property.getElementItemAt("value", propertyValue$.trim());
        if (map$ == null) {
            LOGGER.severe(":indx_listEntities:cannot find map at value=" + propertyValue$ + " in property="
                    + propertyName$);
            return null;

        }
        Sack map = getMember("property.map.base", map$);
        if (map == null) {
            LOGGER.severe(":indx_listEntities:cannot find map=" + map$ + " at value=" + propertyValue$
                    + " in property=" + propertyName$);
            return null;

        }
        String[] ea = map.elementList("entity");
        if (ea != null) {
            return ea;
        } else {
            LOGGER.severe(":indx_listEntities:empty map=" + map$ + " at value=" + propertyValue$ + " in property="
                    + propertyName$);
            return null;
        }
    }

    /**
     * Get keys of entities having certain property name assigned. 
     *  @param propertyName$ property name.
     * @return array of entities keys.
     */
    public String[] indx_listEntitiesAtPropertyName(String propertyName$) {
        if ("label".equals(propertyName$)) {
            return quickMap.elementListNoSorted("label");
        }
        try {
            String property$ = propertyIndex.getElementItemAt("property", propertyName$);

            if (property$ == null) {
                LOGGER.severe(":indx_listEntitiesAtPropertyName:cannot find property in property index  property ="
                        + propertyName$);
                return null;
            }
            Sack property = getMember("property.base", property$);
            if (property == null) {
                LOGGER.severe(":indx_listEntitiesAtPropertyName:cannot find property =" + property$);
                return null;
            }
            Stack<String> s = new Stack<String>();
            Stack<String> s2 = new Stack<String>();
            if ("label".equals(propertyName$)) {
                Core[] ca = property.elementGet("value");
                if (ca != null)
                    for (Core aCa : ca)
                        if (aCa.value != null)
                            s.push(aCa.value);
            } else {
                String[] ma = property.elementList("value");
                if (ma == null) {
                    LOGGER.severe(":indx_listEntitiesAtPropertyName:no values in property =" + property$);
                    return null;
                }

                Sack map;
                String[] ea;
                for (int i = 0; i < ma.length; i++) {
                    s2.clear();
                    map = getMember("property.map.base", property.getElementItemAt("value", ma[i]));
                    if (map == null) {
                        LOGGER.severe(":indx_listEntitiesAtPropertyName:cannot get map[" + i + "]=" + ma[i]);
                        continue;
                    }
                    ea = map.elementList("entity");
                    if (ea == null) {
                        LOGGER.severe(":indx_listEntitiesAtPropertyName:empty map[" + i + "]=" + ma[i]);
                        continue;
                    }
                    for (String anEa : ea) {
                        if (!touchEntity(anEa))
                            s.push(anEa);
                    }
                }
            }
            int cnt = s.size();
            if (cnt < 1) {
                LOGGER.severe(":indx_listEntitiesAtPropertyName:no entities found");
                return null;
            }
            String[] sa = new String[cnt];
            for (int i = 0; i < cnt; i++)
                sa[i] = s.pop();
            return sa;
        } catch (Exception e) {
            LOGGER.severe(":indx_listEntitiesAtPropertyName:" + e.toString());
            return null;
        }
    }

    /**
     * Remove broken property indexes. 
     */
    public void prp_deleteWrongEntries() {
        String[] sa = indx_listPropertyNames();
        if (sa != null)
            for (String aSa : sa)
                prp_deleteWrongPropertyEntries(aSa);
    }

    /**
     * Remove broken property values by property name. 
     * @param propertyName$ property name.
     */
    public void prp_deleteWrongPropertyEntries(String propertyName$) {
        String[] sa = indx_listPropertyValues(propertyName$);
        if (sa != null) {
            for (String aSa : sa)
                prp_deleteWrongValueEntries(propertyName$, aSa);
            sa = indx_listPropertyValues(propertyName$);
            if (sa == null)
                prp_deletePropertyName(propertyName$);
        } else
            prp_deletePropertyName(propertyName$);

    }

    private void prp_deleteWrongValueEntries(String propertyName$, String propertyValue$) {
        try {
            String property$ = propertyIndex.getElementItemAt("property", propertyName$);
            Sack property = getMember("property.base", property$);
            Sack map = getMember("property.map.base", property.getElementItemAt("value", propertyValue$));
            if (map == null) {
                prp_deletePropertyValue(propertyName$, propertyValue$);
                return;
            }
            String[] ea = map.elementList("entity");
            Sack entity = null;
            if (ea != null) {
                for (String anEa : ea) {
                    try {
                        entity = getMember("entity.base", anEa);
                        if (entity.getProperty("label") == null || "null".equals(entity.getProperty("label"))) {
                            deleteEntity(entity);
                            entity = null;
                        }
                    } catch (Exception ee) {
                        LOGGER.severe(":prp_deleteWrongValueEntries:" + ee.toString());
                    }
                    if (entity == null) {
                        map.removeElementItem("entity", anEa);
                    }
                }

            }
            ea = map.elementList("entity");
            if (ea != null)
                return;
            else {
                prp_deletePropertyValue(propertyName$, propertyValue$);
                delete(map);
            }
            save(map);
        } catch (Exception e) {

            LOGGER.severe(":prp_deleteWrongValueEntries:" + e.toString());
        }
    }

    /**
     * Take off the property from the entity. 
     *  @param entity the entity
     *  @param propertyName$ the property name.
     * @return the entity.
     */
    public Sack ent_takeOffProperty(Sack entity, String propertyName$) {
        if (entity == null) {
            LOGGER.severe(":ent_takeOffProperty:entity is null");
            return null;
        }
        if ("label".equals(propertyName$)) {
            LOGGER.severe(":ent_takeOffProperty:cannot delete label");
            return entity;
        }
        if (propertyName$ == null) {
            LOGGER.severe(":ent_takeOffProperty:property name is null");
            return entity;
        }
        //check entity
        Core[] ca = entity.elementGet("property");
        String map$ = null;
        String value$ = null;
        boolean modified = false;
        if (ca == null) {
            LOGGER.severe(":ent_takeOffProperty:no properties in entity=" + entity.getKey());
            return entity;
        } else {
            for (Core aCa : ca) {
                if (propertyName$.equals(aCa.type)) {
                    entity.removeElementItem("property", aCa.name);
                    modified = true;
                    map$ = aCa.name;
                    value$ = aCa.value;
                }
            }

            if (modified)
                save(entity);
            else
                return entity;
        }
        Sack map = getMember("property.map.base", map$);
        if (map == null)
            return entity;
        map.removeElementItem("entity", entity.getKey());
        String[] ea = map.elementListNoSorted("entity");
        if (ea != null && ea.length > 0) {
            save(map);
            return entity;
        }

        //empty value;
        String property$ = map.getAttributeAt("property");
        if (property$ == null) {
            LOGGER.severe(":ent_takeOffProperty:cannot get property=" + propertyName$
                    + ": cannot get property key for map=" + map$ + " entity=" + entity.getProperty("label"));
            return entity;
        }
        Sack property = getMember("property.base", property$);
        if (property == null) {
            LOGGER.severe(":ent_takeOffProperty: property=" + propertyName$ + ": cannot get property at key="
                    + property$);
            return entity;
        }
        property.removeElementItem("value", value$);
        delete(map);
        Core[] ma = property.elementGet("value");
        if (ma != null && ma.length > 0) {
            save(property);
            return entity;
        }
        propertyIndex.removeElementItem("property", propertyName$);
        save(propertyIndex);
        return entity;
    }

    /**
     * Rebuild index.
     *  @param indicator instance of Indicator class
     *  intended to inform caller about progress. Can be null.
     */
    public void indx_rebuild(Indicator indicator) {
        clr_all();
        String[] sa = indx_listEntities();
        progress = 0;
        if (sa == null)
            return;
        if (quickMap == null) {
            quickMap = new Sack();
            quickMap.createElement("label");
            quickMap.createElement("key");
        }
        File headers = new File(entihome$ + "/" + HEADERS);
        if (headers.exists()) {
            if (headers.isDirectory())
                FileExpert.clear(headers.getPath());
        } else {
            headers.mkdir();
        }
        Sack candidate;
        for (String aSa : sa) {
            if (indicator != null) {
                indicator.run();
            }
            candidate = getMember("entity.base", aSa);
            if (candidate == null) {
                try {
                    String base$ = ENTITY_BASE;
                    String file$ = entihome$ + "/" + base$ + "/data/" + aSa;
                    File file = new File(file$);
                    file.delete();
                } catch (Exception ee) {
                    LOGGER.severe(":indx_rebuild:cannot delete unreadable entity=" + aSa);
                }
                continue;
            }
            ent_reindex(candidate);
        }
    }

    /**
     * Rebuild  index entries for the entity. 
     *  @param entity the entity.
     *  @return the entity.
     */
    public Sack ent_reindex(Sack entity) {
        progress++;

        if (entity == null) {
            LOGGER.severe(":ent_reindex:argument is null");
            return null;
        }
        Core[] ca = entity.elementGet("property");
        if (ca == null || ca.length < 1) {
            deleteEntity(entity);
            LOGGER.severe(":ent_reindex:no properties in entity=" + entity.getKey());
            return null;
        }
        String label$ = null;
        String key$ = null;
        Sack candidate;
        for (Core aCa : ca) {
            if (aCa.type != null && aCa.value != null && !"label".equals(aCa.type)) {
                entity = ent_assignProperty(entity, aCa.type, aCa.value);
            }
            if ("label".equals(aCa.type)) {
                label$ = aCa.value;
                entity.removeElementItem("property", aCa.name);
            }
        }
        if (quickMap == null) {
            quickMap = new Sack();
            quickMap.createElement("label");
            quickMap.createElement("key");
        }
        if (label$ == null)
            label$ = entity.getKey();
        key$ = indx_keyAtLabel(label$);
        if (key$ != null) {
            candidate = getMember("entity.base", key$);
            if (candidate != null)
                if (!entity.getKey().equals(key$))
                    label$ = label$ + entity.getKey().substring(0, 4);
        }
        entity.putElementItem("property", new Core("label", entity.getKey(), label$));
        quickMap.putElementItem("label", new Core(entity.getAttributeAt("icon"), label$, entity.getKey()));
        quickMap.putElementItem("key", new Core(label$, entity.getKey(), entity.getProperty("entity")));
        Sack header = null;
        String header$ = getEntihome() + "/" + HEADERS + "/" + entity.getKey();
        try {
            File headerFile = new File(header$);
            //System.out.println("Entigrator:ent_reindex:header="+header$);
            if (!headerFile.exists()) {
                header = new Sack();
                header.createElement("label");
                header.createElement("key");
                header.setKey(entity.getKey());
                header.setPath(header$);
                header.putElementItem("label",
                        new Core(entity.getAttributeAt("icon"), entity.getProperty("label"), entity.getKey()));
                header.putElementItem("key",
                        new Core(entity.getProperty("label"), entity.getKey(), entity.getProperty("entity")));
                header.putAttribute(new Core(null, TIMESTAMP, String.valueOf(System.currentTimeMillis())));
                header.saveXML(header$);
            }
        } catch (Exception e) {
            //System.out.println("Entigrator:ent_reindex:"+e.toString());
            LOGGER.severe(":ent_reindex:" + e.toString());
        }
        entity.putAttribute(new Core(null, "key", entity.getKey()));
        save(entity);
        if (entity.getProperty("entity") != null)
            entity = ent_assignProperty(entity, entity.getProperty("entity"), entity.getProperty("label"));
        entity = col_clearComponents(entity);
        entity = col_updateContainers(entity);
        return entity;
    }

    private boolean ent_propertyAlreadyAssigned(Sack entity, String propertyName$, String propertyValue$) {
        if (entity == null || propertyName$ == null || propertyValue$ == null) {
            //LOGGER.info(":ent_propertyAlreadyAssigned:null argument");
            return false;
        }
        if (propertyIndex.getElementItem("property", propertyName$) == null) {
            indx_addPropertyName(propertyName$);
            // LOGGER.info(":ent_propertyAlreadyAssigned:not assigned yet");
            return false;
        }

        if (!propertyValue$.equals(entity.getProperty(propertyName$))) {
            //LOGGER.info(":ent_propertyAlreadyAssigned:not assigned yet");
            return false;
        }
        String[] sa = indx_listEntities(propertyName$, propertyValue$);
        if (sa == null || sa.length < 1) {
            //LOGGER.info(":ent_propertyAlreadyAssigned:not assigned yet");
            return false;
        }
        Sack propertyMap = indx_getPropertyMap(propertyName$, propertyValue$);
        if (propertyMap == null) {
            LOGGER.severe(":ent_propertyAlreadyAssigned:cannot find property map property name=" + propertyName$
                    + " value=" + propertyValue$);
            return false;
        }
        Core key;
        for (String aSa : sa) {
            key = quickMap.getElementItem("key", aSa);
            if (key == null)
                propertyMap.removeElementItem("entity", aSa);
        }
        save(propertyMap);
        sa = propertyMap.elementList("entity");
        if (sa == null) {
            //LOGGER.info(":ent_propertyAlreadyAssigned:not assigned yet"); 
            return false;
        }
        String key$ = entity.getKey();
        for (String aSa : sa)
            if (key$.equals(aSa)) {
                //LOGGER.info(":ent_propertyAlreadyAssigned:already assigned"); 
                return true;
            }
        //LOGGER.info(":ent_propertyAlreadyAssigned:not assigned yet"); 
        return false;
    }

    /**
     * Assign property to the entity. 
     *  @param entity the entity
     *  @param propertyName$ property name
     *  @param propertyValue$ property value.
     *  @return the entity.
     */
    public Sack ent_assignProperty(Sack entity, String propertyName$, String propertyValue$) {
        if (entity == null || propertyName$ == null || propertyValue$ == null) {
            LOGGER.severe(":ent_assignProperty:null argument");
            return entity;
        }
        if (ent_propertyAlreadyAssigned(entity, propertyName$, propertyValue$)) {
            return entity;
        }
        if ("label".equals(propertyName$)) {
            return ent_assignLabel(entity, propertyValue$);
        }
        if (entity.getProperty(propertyName$) != null)
            entity = ent_takeOffProperty(entity, propertyName$);
        int entStatus = prp_detectAtEntity(entity, propertyName$, propertyValue$);
        int indxStatus = prp_detectAtIndex(entity, propertyName$, propertyValue$);
        //  LOGGER.info(":ent_assignProperty:property="+propertyName$+" value="+propertyValue$+"  entity="+entity.getKey()+", status  entity="+entStatus+" index="+indxStatus);
        switch (entStatus) {
        case ENT_OK:
            switch (indxStatus) {
            case INDX_OK:
                return entity;
            case INDX_NO_ENTITY_ENTRY:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_NO_MAP_ENTRY:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_NO_MAP_SACK:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_NO_PROP_ENTRY:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_NO_PROP_SACK:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_ENTITY_FALSE_MAP:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            }
        case ENT_MULTIPLE_VALUES_OK:
            switch (indxStatus) {
            case INDX_OK:
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_ENTITY_ENTRY:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_MAP_ENTRY:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_MAP_SACK:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_PROP_ENTRY:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_PROP_SACK:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_ENTITY_FALSE_MAP:
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            }
        case ENT_MULTIPLE_VALUES_BAD:
            switch (indxStatus) {
            case INDX_OK:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_ENTITY_ENTRY:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_MAP_ENTRY:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_MAP_SACK:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_PROP_ENTRY:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_PROP_SACK:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_ENTITY_FALSE_MAP:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            }
        case ENT_PROP_NOT_ASSIGNED:
            switch (indxStatus) {
            case INDX_OK:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_deleteWrongProperties(entity);
                return get(entity);
            case INDX_NO_ENTITY_ENTRY:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_NO_MAP_ENTRY:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_NO_MAP_SACK:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_NO_PROP_ENTRY:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_NO_PROP_SACK:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            case INDX_ENTITY_FALSE_MAP:
                ent_assignPropertyEntry(entity, propertyName$, propertyValue$);
                ent_assignMapEntry(entity, propertyName$, propertyValue$);
                return get(entity);
            }
        }
        return get(entity);
    }

    /**
     * Clone an entity. 
     *  @param template the original entity.
     *  @return the clone entity.
     */
    public Sack ent_clone(Sack template) {
        if (template == null)
            return null;
        return ent_clone(template, template.getProperty("label"));
    }

    /**
     * Clone an entity. 
     *  @param template the original entity
     *  @param label$ the label of the clone entity.
     *  @return the clone entity.
     */
    public Sack ent_clone(Sack template, String label$) {
        if (template == null)
            return null;
        Sack entity = new Sack();
        entity.putAttribute(new Core(null, "residence.base", Entigrator.ENTITY_BASE));
        entity.putAttribute(new Core(null, "template", template.getKey()));
        if (indx_keyAtLabel(label$) != null)
            label$ = label$ + Identity.key().substring(0, 4);
        Core core = template.getAttribute("icon");
        if (core != null)
            entity.putAttribute(core);
        core = new Core(null, "alias", label$);
        entity.putAttribute(core);
        Core[] ca;
        String[] ea = template.elementsListNoSorted();
        if (ea != null) {
            for (int i = 0; i < ea.length; i++) {
                try {
                    entity.createElement(ea[i]);
                    ca = template.elementGet(ea[i]);
                    entity.elementReplace(ea[i], ca);

                } catch (Exception e) {
                    // System.out.println("Entigrator:ent_clone:ea[" + i + "]=" + ea[i] + ":" + e.toString());
                    LOGGER.severe(":ent_clone:ea[" + i + "]=" + ea[i] + ":" + e.toString());
                }
            }
        }
        // System.out.println("Entigrator:ent_clone:clone");
        // entity.print();
        save(entity);
        ca = entity.elementGet("property");
        for (Core aCa : ca) {
            if ("template".equals(aCa.type)) {
                continue;
            }
            if ("label".equals(aCa.type))
                entity = ent_assignLabel(entity, label$);
            else
                entity = ent_assignProperty(entity, aCa.type, aCa.value);
        }
        save(entity);
        return entity;
    }

    private void ent_deleteWrongProperties(Sack entity) {
        if (entity == null) {
            LOGGER.severe(":ent_deleteWrongProperties:null argument");
            return;
        }
        Core[] ca = entity.elementGet("wrong.props");
        if (ca == null)
            return;
        Sack map;
        for (Core aCa : ca) {
            map = indx_getPropertyMap(aCa.type, aCa.value);
            if (map == null)
                continue;
            map.removeElementItem("entity", entity.getKey());
            save(map);
            entity.removeElementItem("wrong.props", aCa.name);
        }
        save(entity);
        ent_reindex(entity);
    }

    private void ent_assignPropertyEntry(Sack entity, String propertyName$, String propertyValue$) {
        if (entity == null || propertyName$ == null || propertyValue$ == null) {
            LOGGER.severe(":ent_assignPropertyEntry:null argument");
            return;
        }
        if ("label".equals(propertyName$))
            entity.putAttribute(new Core("String", "alias", propertyValue$));
        Sack map = indx_getPropertyMap(propertyName$, propertyValue$);
        if (map == null) {
            LOGGER.severe(":ent_assignPropertyEntry:cannot get map at property=" + propertyName$ + " value="
                    + propertyValue$);
            return;
        }
        entity.putElementItem("property", new Core(propertyName$, map.getKey(), propertyValue$));
        save(entity);
    }

    /**
     * Assign icon file to the entity. 
     *  @param entity the entity
     *  @param icon$ the file name.
     *  @return the entity.
     */
    public Sack ent_assignIcon(Sack entity, String icon$) {
        try {
            entity.putAttribute(new Core(null, "icon", icon$));
            quickMap.putElementItem("label", new Core(icon$, entity.getProperty("label"), entity.getKey()));
            save(entity);

        } catch (Exception e) {
            LOGGER.severe(":ent_assignIcon:" + e.toString());
        }

        return entity;
    }

    /**
     * Assign a label to the entity. 
     *  @param entity the entity
     *  @param label$ the label.
     * @return the entity.
     */

    public Sack ent_assignLabel(Sack entity, String label$) {
        try {
            Core[] ca = entity.elementGet("property");
            if (ca != null)
                for (Core aCa : ca)
                    if ("label".equals(aCa.type))
                        entity.removeElementItem("property", aCa.name);
            String newLabel$ = label$;
            Core old = quickMap.getElementItem("label", label$);
            String key$ = entity.getKey();
            //String icon$=entity.getAttributeAt("icon");
            if (old != null && !key$.equals(old.value))
                newLabel$ = label$ + Identity.key().substring(0, 4);
            entity.putElementItem("property", new Core("label", key$, newLabel$));
            saveNative(entity);

        } catch (Exception e) {
            LOGGER.severe(":ent_assignLabel:" + e.toString());
        }
        return entity;
    }

    private void ent_assignMapEntry(Sack entity, String propertyName$, String propertyValue$) {

        if (entity == null || propertyName$ == null || propertyValue$ == null) {
            LOGGER.severe(":ent_assignMapEntry:null argument");
            return;
        }

        if ("label".equals(propertyName$)) {
            ent_assignLabel(entity, propertyValue$);
            return;
        }
        Sack map = indx_getPropertyMap(propertyName$, propertyValue$);
        if (map == null) {
            LOGGER.severe(
                    ":ent_assignMapEntry:cannot get map at property=" + propertyName$ + " value=" + propertyValue$);
            return;
        }
        map.putElementItem("entity",
                new Core(entity.getProperty("entity"), entity.getKey(), entity.getProperty("label")));
        save(map);
    }

    private int prp_detectAtIndex(Sack entity, String propertyName$, String propertyValue$) {
        int ret = WRONG_ARG;
        if (entity == null || propertyName$ == null || propertyValue$ == null)
            return ret;
        String property$ = propertyIndex.getElementItemAt("property", propertyName$);
        if (property$ == null)
            return INDX_NO_PROP_ENTRY;
        Sack property = getMember("property.base", property$);
        if (property == null)
            return INDX_NO_PROP_SACK;
        String map$ = property.getElementItemAt("value", propertyValue$);
        if (map$ == null)
            return INDX_NO_MAP_ENTRY;
        Sack map = getMember("property.map.base", map$);
        if (map == null)
            return INDX_NO_MAP_SACK;
        Core core = map.getElementItem("entity", entity.getKey());
        if (core == null)
            return INDX_NO_ENTITY_ENTRY;
        int INDX_FALSE_ENTITY_ENTRY = 10;
        if ("false".equals(core.value))
            return INDX_FALSE_ENTITY_ENTRY;
        core = entity.getElementItem("property", map$);
        if (core == null)
            return INDX_ENTITY_FALSE_MAP;
        return INDX_OK;
    }

    private int prp_detectAtEntity(Sack entity, String propertyName$, String propertyValue$) {
        int ret = WRONG_ARG;
        if (entity == null || propertyName$ == null || propertyValue$ == null)
            return ret;
        String[] pa = entity.listItemsAtType("property", propertyName$);
        if (pa == null)
            return ENT_PROP_NOT_ASSIGNED;

        Core core;

        for (String aPa : pa) {
            core = entity.getElementItem("property", aPa);
            if (propertyValue$.equals(core.value)) {
                if ("label".equals(propertyName$)) {
                    entity.putAttribute(new Core("String", "alias", propertyValue$));
                    save(entity);
                }
                if (pa.length == 1) {
                    if (!entity.existsElement("wrong.props"))
                        return ENT_OK;
                    if (entity.elementListNoSorted("wrong.props") == null
                            || entity.elementListNoSorted("wrong.props").length < 1)
                        return ENT_OK;
                    ret = ENT_MULTIPLE_VALUES_OK;

                }
            } else {
                if (!entity.existsElement("wrong.props"))
                    entity.createElement("wrong.props");
                entity.putElementItem("wrong.props", core);
                if (ret != ENT_MULTIPLE_VALUES_OK)
                    ret = ENT_MULTIPLE_VALUES_BAD;
            }
        }
        if (entity.existsElement("wrong.props")) {

            String[] sa = entity.elementListNoSorted("wrong.props");
            if (sa != null)
                for (String aSa : sa) {
                    entity.removeElementItem("property", aSa);
                }
            save(entity);
        }

        return ret;
    }

    /**
     * Remove broken component links
     *  @param container the entity.
     * @return the entity.
     */

    public Sack col_clearComponents(Sack container) {
        try {
            Core[] ca = container.elementGet("component");
            if (ca == null)
                return container;
            ArrayList<Core> cl = new ArrayList<Core>();
            for (Core aCa : ca) {
                if (indx_getLabel(aCa.value) != null)
                    cl.add(aCa);
                else {
                    Sack component = getEntityAtKey(aCa.value);
                    if (component != null)
                        cl.add(aCa);
                }
            }
            ca = cl.toArray(new Core[0]);
            container.elementReplace("component", ca);
            save(container);
            return container;
        } catch (Exception e) {
            LOGGER.severe(":col_clearComponents" + e.toString());
            return container;

        }
    }

    /**
     * Rebuild component - container links.
     *  @param component the entity.
     * @return the entity.
     */
    public Sack col_updateContainers(Sack component) {
        try {
            Core[] ca = component.elementGet("container");
            if (ca == null)
                return component;
            Sack container;
            String componentLabel$ = component.getProperty("label");
            String componentKey$ = component.getKey();
            for (Core aCa : ca) {
                container = getEntityAtKey(aCa.value);
                if (container != null) {
                    container.putElementItem("component", new Core(componentLabel$, aCa.name, componentKey$));
                    save(container);
                } else {
                    component.removeElementItem("container", aCa.name);
                }

            }
            save(component);

        } catch (Exception e) {
            LOGGER.severe(":col_clearComponents" + e.toString());

        }
        return component;
    }

    /**
     * Add component to the entity
     *  @param container the container entity
     *  @param component the component entity.
     * @return the key of the link record.
     */
    public String col_addComponent(Sack container, Sack component) {

        if (container == null) {
            LOGGER.severe(":col_addComponent:container is null");
            return null;
        }
        col_clearComponents(container);
        if (component == null) {
            //   LOGGER.severe(":col_addComponent:component is null");
            return null;
        }

        if (container.getKey().equals(component.getKey())) {
            //   LOGGER.severe(":col_addComponent:component cannot be equal container");
            return null;
        }
        if (col_existsRelation(container, component)) {
            //System.out.println("Entigrator:col_addComponent:already exists");
            //LOGGER.info(":col_addComponent:already component");
            Core[] ca = component.elementGet("container");
            if (ca != null)
                for (Core aCa : ca)
                    if (container.getKey().equals(aCa.value))
                        return aCa.name;
        }
        if (!component.existsElement("container"))
            component.createElement("container");
        if (!container.existsElement("component"))
            container.createElement("component");
        if (!container.existsElement("component.type"))
            container.createElement("component.type");
        String key$ = gdt.data.grain.Identity.key();
        Core componentRecord = new Core(component.getProperty("label"), key$, component.getKey());
        container.putElementItem("component", componentRecord);
        Core containerRecord = new Core(container.getProperty("label"), key$, container.getKey());
        component.putElementItem("container", containerRecord);
        container.putElementItem("component.type",
                new Core(component.getProperty("entity"), key$, component.getProperty("component")));
        save(component);
        save(container);
        return key$;
    }

    private boolean col_existsComponent(Sack container, Sack component) {
        if (container == null)
            return false;
        if (component == null)
            return false;
        //if (tag$ == null)
        //   return false;
        Core[] ca = container.elementGet("component");
        if (ca == null)
            return false;
        for (Core aCa : ca)
            if (aCa.type != null)
                // if (ca[i].type.equals(tag$))
                if (component.getKey().equals(aCa.value))
                    return true;
        return false;
    }

    private boolean col_existsContainer(Sack container, Sack component) {
        if (container == null)
            return false;
        if (component == null)
            return false;
        Core[] ca = component.elementGet("container");
        if (ca == null)
            return false;
        for (Core aCa : ca)
            if (aCa.type != null)
                if (container.getKey().equals(aCa.value))
                    return true;
        return false;
    }

    /**
     * Check if the entity is a component of the container entity
     *  or its containers.
     *  @param container the container entity
     *  @param component the component entity.
     * @return true if the component entity is really a component of one container
     * , false otherwise.
     */
    public boolean col_isComponentUp(Sack container, Sack component) {
        if (container == null)
            return false;
        if (component == null)
            return false;
        if (container.getKey().equals(component.getKey()))
            return true;
        Core[] ca = container.elementGet("component");
        Sack candidate;
        if (ca != null)
            for (Core aCa : ca) {
                if (component.getKey().equals(aCa.value))
                    return true;

            }
        ca = container.elementGet("container");
        if (ca != null) {

            for (Core aCa : ca) {
                candidate = getMember("entity.base", aCa.value);
                if (candidate != null) {
                    if (col_isComponentUp(candidate, component))
                        return true;
                }
            }
        }
        return false;
    }

    /**
     * Check if the entity is a component of the container entity
     *  or its components.
     *  @param container the container entity
     *  @param component the component entity.
     * @return true if the component entity is really a component of one container
     * , false otherwise.
     */
    public boolean col_isComponentDown(Sack container, Sack component) {
        if (container == null)
            return false;
        if (component == null)
            return false;
        Core[] ca = container.elementGet("component");
        Sack candidate;
        if (ca != null)
            for (Core aCa : ca) {
                if (component.getKey().equals(aCa.value))
                    return true;
                else {
                    candidate = getMember("entity.base", aCa.value);
                    if (candidate != null) {
                        // System.out.println("Entigrator:col_isComponent: container="+container.getProperty("label")+" component="+component.getProperty("label")+" candidate="+candidate.getProperty("label"));   
                        if (col_isComponentDown(candidate, component))
                            return true;
                    }
                }
            }
        return false;
    }

    /**
     * Add property name into the index
     *  @param propertyName$ the property name
     * @return the property name map.
     */
    public Sack indx_addPropertyName(String propertyName$) {

        if (propertyName$ == null) {
            LOGGER.severe(":indx_addPropertyName:property name is null");
            return null;
        }
        if ("label".equals(propertyName$)) {
            LOGGER.severe(":indx_addPropertyName:property name is 'label'");
            return null;
        }
        if (propertyIndex.getElementItemAt("property", propertyName$) != null) {
            String property$ = propertyIndex.getElementItemAt("property", propertyName$);
            Sack property = getMember("property.base", property$);
            if (property != null)
                return property;
            else {
                propertyIndex.removeElementItem("property", propertyName$);
            }
        }

        Sack property = new Sack();
        property.putAttribute(new Core("key", "residence.base", PROPERTY_BASE));
        property.putAttribute(new Core("String", "alias", "property." + propertyName$));
        property.putAttribute(new Core("key", "property.name", propertyName$));
        property.createElement("value");
        propertyIndex.putElementItem("property", new Core("key", propertyName$, property.getKey()));
        save(property);
        save(propertyIndex);
        return property;
    }

    private void clr_maps() {
        try {
            String mapBase$ = entihome$ + "/" + PROPERTY_MAP + "/data";
            String propertyBase$ = entihome$ + "/" + PROPERTY_BASE + "/data";
            File mapBase = new File(mapBase$);
            String[] sa = mapBase.list();
            if (sa == null || sa.length < 1)
                return;
            Sack map;
            String property$;
            File propFile;

            for (String aSa : sa) {
                map = Sack.parseXML(mapBase$ + "/" + aSa);
                if (map == null)
                    try {
                        new File(mapBase$ + "/" + aSa).delete();
                        continue;
                    } catch (Exception ee) {
                        LOGGER.severe(":clr_maps:file=" + aSa + ":" + ee.toString());
                    }
                property$ = map.getAttributeAt("property");
                if (property$ == null) {
                    try {
                        new File(mapBase$ + "/" + aSa).delete();
                        LOGGER.severe(":clr_maps:wrong property:delete map=" + aSa);
                        continue;
                    } catch (Exception ee) {
                        LOGGER.severe(":clr_maps:file=" + aSa + ":" + ee.toString());
                    }
                }
                try {
                    propFile = new File(propertyBase$ + "/" + property$);
                    if (propFile.exists())
                        continue;
                } catch (Exception ee) {
                    LOGGER.severe(":clr_maps:file=" + aSa + ":" + ee.toString());
                }
                try {
                    new File(mapBase$ + "/" + aSa).delete();

                } catch (Exception ee) {
                    LOGGER.severe(":clr_maps:file=" + aSa + ":" + ee.toString());
                }
            }
        } catch (Exception e) {
            LOGGER.severe(":clr_maps:" + e.toString());
        }
    }

    private void clr_properties() {
        try {
            if (propertyIndex == null) {
                LOGGER.severe(":clr_properties:property index is null");
                return;
            }
            String propertyBase$ = entihome$ + "/" + PROPERTY_BASE + "/data";
            File propertyBase = new File(propertyBase$);
            String[] sa = propertyBase.list();
            if (sa == null || sa.length < 1)
                return;

            Sack property;
            String property$;
            for (String aSa : sa) {
                property = Sack.parseXML(propertyBase$ + "/" + aSa);
                if (property == null)
                    try {
                        new File(propertyBase$ + "/" + aSa).delete();

                        continue;
                    } catch (Exception ee) {
                        LOGGER.severe(":clr_properties:file=" + aSa + ":" + ee.toString());
                    }
                property$ = property.getAttributeAt("property.name");
                if (property$ == null) {
                    try {
                        new File(propertyBase$ + "/" + aSa).delete();
                        continue;
                    } catch (Exception ee) {
                        LOGGER.severe(":clr_properties:file=" + aSa + ":" + ee.toString());
                    }
                }
                String key$ = propertyIndex.getElementItemAt("property", property$);
                if (key$ == null || !key$.equals(property.getKey()))
                    try {
                        new File(propertyBase$ + "/" + aSa).delete();
                    } catch (Exception ee) {
                        LOGGER.severe(":clr_properties:file=" + aSa + ":" + ee.toString());
                    }

            }
        } catch (Exception e) {
            LOGGER.severe(":clr_properties:" + e.toString());
        }
    }

    private void clr_all() {
        clr_index();
        clr_properties();
        clr_maps();
    }

    private void clr_index() {
        if (propertyIndex == null)
            return;
        Core[] ca = propertyIndex.elementGet("property");
        if (ca == null || ca.length < 1)
            return;
        Sack property = null;
        for (Core aCa : ca) {
            try {
                property = getMember("property.base", aCa.value);
            } catch (Exception ee) {
                LOGGER.severe(":clr_index:" + ee.toString());
            }
            if (property == null)
                propertyIndex.removeElementItem("property", aCa.name);
        }
        try {
            propertyIndex.saveXML(getEntihome() + "/_OMw7Msp2wcy5tMEmoayqGzwIJE8");
        } catch (Exception e) {
            LOGGER.severe(":clr_index:" + e.toString());
        }
    }

    /**
     * Add property value into the index
     *  @param propertyName$ the property name
     *  @param propertyValue$ the property value
     * @return the key of the property value map.
     */
    public String indx_addPropertyValue(String propertyName$, String propertyValue$) {
        if ("label".equals(propertyName$)) {
            LOGGER.severe(":indx_addPropertyValue:cannot add property='label'");
            return null;
        }
        if (propertyName$ == null || propertyValue$ == null) {
            LOGGER.severe(":indx_addPropertyValue:property name or value is null");
            return null;
        }
        Sack property = null;
        if (propertyIndex.getElementItemAt("property", propertyName$) != null)
            property = getMember("property.base", propertyIndex.getElementItemAt("property", propertyName$));
        String map$ = null;
        if (property == null) {

            propertyIndex.removeElementItem("property", propertyName$);
            property = indx_addPropertyName(propertyName$);
            if (property == null) {
                LOGGER.severe(":indx_addPropertyValue:cannot find/create property=" + propertyName$);
                return null;
            }
        } else {
            map$ = property.getElementItemAt("value", propertyValue$);
            if (map$ != null) {
                Sack map = getMember("property.map.base", map$);
                if (map != null) {
                    return map$;
                }
            }
        }
        property.removeElementItem("value", propertyValue$);
        Sack propertyMap = new Sack();
        propertyMap.putAttribute(new Core("key", "residence.base", PROPERTY_MAP));
        propertyMap.putAttribute(new Core("key", "property", property.getKey()));
        propertyMap.putAttribute(new Core("key", "property.name", propertyName$));
        propertyMap.putAttribute(new Core("key", "property.name", propertyValue$));
        propertyMap.putAttribute(new Core("key", "icon", "property_list.gif"));
        propertyMap.putAttribute(new Core("String", "alias", "property." + propertyName$ + "" + propertyValue$));
        propertyMap.createElement("entity");
        property.putElementItem("value", new Core("key", propertyValue$, propertyMap.getKey()));
        save(property);
        save(propertyMap);
        return propertyMap.getKey();
    }

    /**
     * Delete property value from the index and all entities.
     *  @param propertyName$ the property name
     *  @param propertyValue$ the property value
     */
    public void prp_deletePropertyValue(String propertyName$, String propertyValue$) {
        if ((propertyName$ == null) || (propertyValue$ == null)) {
            LOGGER.severe(":prp_deletePropertyValue:argument is null");
            return;
        }
        Sack property = indx_getProperty(propertyName$);
        if (property == null) {
            LOGGER.severe(":prp_deletePropertyValue:cannot find property");
            return;
        }
        String map$ = property.getElementItemAt("value", propertyValue$);
        if (map$ != null) {
            prp_deleteValue(map$);
        }
        property.removeElementItem("value", propertyValue$.trim());
        save(property);
    }

    /**
     * Replace property value in the index .
     *  @param propertyName$ the property name
     *  @param propertyValue$ the old property value
     *  @param newPropertyValue$ the new property value.
     */
    public void prp_editPropertyValue(String propertyName$, String propertyValue$, String newPropertyValue$) {
        if ((propertyName$ == null) || (propertyValue$ == null)) {
            LOGGER.severe(":prp_editPropertyValue:argument is null");
            return;
        }
        Sack property = indx_getProperty(propertyName$);
        if (property == null) {
            LOGGER.severe(":prp_deletePropertyValue:cannot find property");
            return;
        }
        Core map = property.getElementItem("value", propertyValue$);
        if (map == null) {
            LOGGER.severe(":prp_editPropertyValue:no map for property name=" + propertyName$ + " value="
                    + propertyValue$);
            return;
        }
        property.removeElementItem("value", propertyValue$.trim());
        property.putElementItem("value", new Core(map.type, newPropertyValue$, map.value));
        save(property);
    }

    /**
     * Delete property from the database.
     *  @param propertyName$ the property name
     */
    public void prp_deletePropertyName(String propertyName$) {
        if ((propertyName$ == null || propertyName$.length() < 1)) {
            Core[] ca = propertyIndex.elementGet("property");
            if (ca == null)
                return;
            ArrayList<Core> cl = new ArrayList<Core>();
            for (Core aCa : ca)
                if (aCa.name != null && aCa.name.length() > 0)
                    cl.add(aCa);
            ca = cl.toArray(new Core[0]);
            propertyIndex.elementReplace("property", ca);
            save(propertyIndex);
            return;
        }
        Sack property = indx_getProperty(propertyName$);
        if (property != null) {
            String[] sa = property.elementList("value");
            if (sa != null)
                for (String aSa : sa)
                    prp_deleteValue(property.getElementItemAt("value", aSa));
            delete(property);
        }
        propertyIndex.removeElementItem("property", propertyName$.trim());
        save(propertyIndex);
    }

    private boolean prp_deleteValue(String map$) {
        if (map$ == null) {
            LOGGER.severe(":prp-deleteValue:map is null");
            return false;
        }
        Sack map = getMember("property.map.base", map$);
        if (map == null) {
            LOGGER.severe(":prp-deleteValue:can not find map=" + map$);
            return false;
        }
        Sack property = getMember("property.base", map.getAttributeAt("property"));
        if (property != null) {
            property.removeElementItem("value", property.getElementItemAtValue("value", map$));
            save(property);
        }
        String[] ea = map.elementList("entity");
        {
            if (ea != null) {
                {
                    Sack entity;
                    for (String anEa : ea) {
                        entity = getMember("entity.base", anEa);
                        if (entity == null)
                            continue;
                        entity.removeElementItem("property", map$);
                        save(entity);
                    }
                }
            }
            delete(map);
        }
        return true;
    }

    private void delete(Sack sack) {
        if (sack == null)
            return;
        if (sack.getAttributeAt(PRESERVED) != null) {
            LOGGER.severe(":delete:cannot delete preserved sack");
            return;
        }
        try {
            String record$ = entihome$ + "/" + sack.getKey();
            File record = new File(record$);
            if (record.exists()) {
                FileExpert.delete(record$);
            }
            String base$ = sack.getAttributeAt("residence.base");
            String path$;
            if ("register".equals(base$))
                path$ = entihome$ + "/" + sack.getPath();
            else
                path$ = entihome$ + "/" + base$ + "/data/" + sack.getKey();
            File file = new File(path$);
            file.delete();
        } catch (Exception e) {
            LOGGER.severe(":delete" + e.toString());
        }
    }

    boolean col_existsRelation(Sack container, Sack component) {
        if (component == null || container == null)
            return false;
        if (col_existsComponent(container, component))
            if (col_existsContainer(container, component))
                return true;
        col_breakRelation(container, component);
        return false;
    }

    /**
     * Break container-component relation. 
     *  @param container the container entity
     *  @param component the component entity.
     * @return the container entity.
     */
    public Sack col_breakRelation(Sack container, Sack component) {
        if (container == null || component == null) {
            LOGGER.severe(":col_breakRelation:argument is null");
            return container;
        }
        String component$ = component.getKey();
        String container$ = container.getKey();
        Core[] ca = component.elementGet("container");
        if (ca != null)
            for (Core aCa : ca)
                if (container$.equals(aCa.value))
                    component.removeElementItem("container", aCa.name);
        ca = container.elementGet("component");
        if (ca != null)
            for (Core aCa : ca)
                if (component$.equals(aCa.value)) {
                    container.removeElementItem("component", aCa.name);
                    container.removeElementItem("component.type", aCa.name);
                }
        save(component);
        save(container);
        return container;
    }

    /**
     * Create a new entity 
     *  @param type$ the type(category) of the entity
     *  @param label$ the entity label.
     * @return the created entity.
     */
    public Sack ent_new(String type$, String label$) {
        if (type$ == null || label$ == null) {
            LOGGER.severe(":ent_new:argument is null");
            return null;
        }
        Sack entity = new Sack();
        entity.putAttribute(new Core("key", "icon", "sack.gif"));
        entity.putAttribute(new Core(null, "residence.base", ENTITY_BASE));
        entity.createElement("property");
        entity.putElementItem("property", new Core("label", entity.getKey(), label$));
        entity.putElementItem("property", new Core("entity", Identity.key(), type$));
        saveNative(entity);
        ent_reindex(entity);
        return entity;
    }

    /**
     * Create a new entity 
     *  @param type$ the type(category) of the entity
     *  @param label$ the entity label
     *  @param key$ the key of the entity.
     * @return the created entity.
     */
    public Sack ent_new(String type$, String label$, String key$) {
        if (type$ == null || label$ == null || key$ == null) {
            LOGGER.severe(":ent_new:argument is null");
            return null;
        }
        Sack entity = new Sack();
        entity.setKey(key$);
        entity.putAttribute(new Core("key", "icon", "sack.gif"));
        entity.putAttribute(new Core(null, "residence.base", ENTITY_BASE));
        entity.createElement("property");
        entity.putElementItem("property", new Core("label", entity.getKey(), label$));
        entity.putElementItem("property", new Core("entity", Identity.key(), type$));
        saveNative(entity);
        ent_reindex(entity);
        return entity;
    }

    /**
     * Get the entity by label 
     *  @param label$ the entity label
     * @return the entity or null.
     */
    public Sack ent_getAtLabel(String label$) {
        try {
            String key$ = indx_keyAtLabel(label$);
            return getMember("entity.base", key$);
        } catch (Exception e) {
            LOGGER.severe(":ent_getAtLabel:" + e.toString());
            return null;
        }
    }

    /**
     * Delete an entity from the database. 
     *  @param entity the entity.
     */
    public void deleteEntity(Sack entity) {
        if (entity == null) {
            LOGGER.severe(":deleteEntity:entity is null");
            return;
        }
        String key$ = entity.getKey();
        String[] ra = ent_listContainers(entity);
        try {
            if (ra != null && ra.length > 0) {
                Sack container;
                for (String aRa : ra) {
                    container = getMember("entity.base", aRa);
                    if (container != null)
                        col_breakRelation(container, entity);
                }
            }
        } catch (Exception e) {
            LOGGER.severe(":deleteEntity:" + e.toString());
        }
        ra = ent_listComponents(entity);
        try {
            if (ra != null && ra.length > 0) {
                Sack component;
                for (String aRa : ra) {
                    component = getMember("entity.base", aRa);
                    if (component != null) {
                        col_breakRelation(entity, component);
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.severe(":deleteEntity:" + e.toString());
        }
        if (entity.getAttributeAt(PRESERVED) != null) {
            LOGGER.severe(":deleteEntity:cannot delete preserverd entity");
            return;
        }
        String label$ = null;
        try {
            String[] sa = entity.elementList("property");
            label$ = entity.getProperty("label");
            if (sa != null) {
                Sack map;
                for (String aSa : sa) {
                    map = getMember("property.map.base", aSa);
                    if (map == null)
                        continue;
                    map.removeElementItem("entity", key$);
                    save(map);
                }
            }

            if (label$ != null)
                prp_deletePropertyValue(entity.getProperty("entity"), label$);

        } catch (Exception e) {
            //LOGGER.info(":deleteEntity:"+e.toString());
        }

        try {
            entitiesCache.delete(key$);
            File fname = new File(entihome$ + "/" + ENTITY_BASE + "/data/" + key$);
            fname.delete();
        } catch (Exception ee) {
            // LOGGER.info(":deleteEntity:"+ee.toString());
        }
        try {
            String record$ = getEntihome() + "/" + key$;
            FileExpert.delete(record$);
            if (label$ != null)
                prp_deletePropertyValue("label", label$);
        } catch (Exception ee) {
            //   LOGGER.info(":deleteEntity:"+ee.toString());
        }
        quickMap.removeElementItem("label", label$);
        quickMap.removeElementItem("key", key$);

        try {
            File header = new File(getEntihome() + "/" + HEADERS + "/" + key$);
            if (header.exists())
                header.delete();
        } catch (Exception ee) {
            //  LOGGER.info(":deleteEntity:"+ee.toString());
        }
    }

    /**
     * List containers of the entity 
     *  @param entity the entity.
     * @return the array of keys of containers.
     */
    public String[] ent_listContainers(Sack entity) {
        if (entity == null) {
            // LOGGER.severe(":ent_listContainers:entity is null");
            return null;
        }
        Core[] ca = entity.elementGet("container");
        if (ca == null) {
            // LOGGER.info(":ent_listContainers:no 'container' element in entity=" + entity.getProperty("label"));
            return null;
        }
        Stack<String> s = new Stack<String>();
        Sack container;
        boolean modified = false;
        for (Core aCa : ca) {
            if (entity.getKey().equals(aCa.value))
                continue;
            container = getMember("entity.base", aCa.value);
            if (container == null) {
                entity.removeElementItem("container", aCa.name);
                modified = true;
                continue;
            }
            s.push(aCa.value);
        }
        if (modified)
            save(entity);
        int cnt = s.size();
        if (cnt < 1) {
            //LOGGER.info(":ent_listContainers:empty 'container' element in entity=" + entity.getProperty("label"));
            return null;
        }
        String[] sa = new String[cnt];
        for (int i = 0; i < cnt; i++)
            sa[i] = s.pop();
        return sa;
    }

    /**
     * List all entities in the database. 
     * @return the array of keys of all entities.
     */
    public String[] indx_listEntities() {
        String base$ = entihome$ + "/" + ENTITY_BASE + "/data";
        File entityHome = new File(base$);
        try {
            return entityHome.list();
        } catch (Exception e) {
            LOGGER.severe(":indx_listEntities:" + e.toString());
            return null;
        }
    }

    /**
     * List components of the entity 
     *  @param entity the entity.
     * @return the array of keys of components.
     */
    public String[] ent_listComponents(Sack entity) {
        try {
            if (entity == null)
                return null;
            Core[] ca = entity.elementGet("component");
            if (ca == null)
                return null;
            Stack<String> s = new Stack<String>();
            for (Core aCa : ca) {
                if (entity.getKey().equals(aCa.value))
                    continue;
                s.push(aCa.value);
            }
            int cnt = s.size();
            if (cnt < 1)
                return null;
            String[] sa = new String[cnt];
            for (int i = 0; i < cnt; i++)
                sa[i] = s.pop();
            return sa;
        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).severe(e.toString());
            return null;
        }
    }

    /**
     * List components of the entity and its components
     * recursively. 
     *  @param entity the entity.
     * @return the array of keys of components.
     */
    public String[] ent_listComponentsCascade(Sack entity) {
        if (entity == null)
            return null;

        Stack<String> s = new Stack<String>();
        ent_listComponentsCascade(entity, s);
        int cnt = s.size();
        if (cnt < 1)
            return null;
        String[] sa = new String[cnt];
        for (int i = 0; i < cnt; i++)
            sa[i] = (String) s.pop();
        return sa;
    }

    private void ent_listComponentsCascade(Sack entity, Stack<String> s) {
        String[] sa = ent_listComponents(entity);
        if (sa == null)
            return;
        Sack component;
        for (String aSa : sa) {
            component = getMember("entity.base", aSa);
            if (component == null) {
                continue;
            }
            Support.addItem(aSa, s);
            ent_listComponentsCascade(component, s);
        }
    }

    /**
     * Get the path of the entity home directory 
     *  @param entity$ the entity key.
     * @return the path of the home directory.
     */
    public String ent_getHome(String entity$) {
        return entihome$ + "/" + entity$;
    }

    /**
     * Get the path of parent directory of the database. 
     * @return the path of the database parent directory.
     */
    public String getEntihome() {
        return entihome$;
    }

    /**
     * Get the name of the database directory. 
     * @return the name of the database directory.
     */
    public String getBaseName() {
        String entihome$ = getEntihome();
        if (entihome$ == null)
            return null;
        try {
            File entihome = new File(entihome$);
            return entihome.getName();
        } catch (Exception e) {
            LOGGER.severe(":getBasename:" + e.toString());
            return null;
        }
    }

    private void makeQuickMap() {
        long currentTime = System.currentTimeMillis();
        long qTimestamp = 0;
        if (quickMap != null) {

            try {
                qTimestamp = Long.parseLong(quickMap.getAttributeAt(Entigrator.TIMESTAMP));
            } catch (Exception ee) {
            }
            if ((currentTime - qTimestamp < 100) && quickMap != null) {
                // System.out.println("Entigrator:makeQuickMap:NOPE");
                return;
            }
            if (currentTime - qTimestamp < 1500)
                quickMap = Sack.parseXML(getEntihome() + "/" + QUICK_MAP);
            if (quickMap != null) {
                //System.out.println("Entigrator:makeQuickMap:RELOAD");
                return;
            }
        }

        quickMap = new Sack();

        //System.out.println("Entigrator:makeQuickMap:REBUILD");
        quickMap.setKey(QUICK_MAP);
        String[] sa = indx_listEntities();
        quickMap.createElement("label");
        quickMap.createElement("key");
        if (sa != null) {
            Sack entity;
            File headerFile;
            String label$;
            String headersHome$ = getEntihome() + "/" + HEADERS + "/";
            Sack header;
            for (String aSa : sa) {
                try {
                    headerFile = new File(headersHome$ + aSa);
                    if (!headerFile.exists()) {
                        entity = getMember("entity.base", aSa);
                        if (entity != null) {
                            save(entity);
                            headerFile = new File(headersHome$ + aSa);
                        }
                        if (!headerFile.exists()) {
                            // LOGGER.info(":makeQuickMap:cannot find  header="+aSa);
                            continue;
                        }
                    }
                    header = Sack.parseXML(headerFile.getPath());
                    if (header == null) {
                        //   LOGGER.info(":makeQuickMap:cannot read  header="+header.getPath());
                        continue;
                    }
                    Core key = header.getElementItem("key", aSa);
                    label$ = key.type;
                    Core label = quickMap.getElementItem("label", label$);
                    if (label != null) {
                        entity = getEntity(aSa);
                        label$ = label$ + Identity.key().substring(0, 4);
                        entity = ent_assignLabel(entity, label$);
                        entity = ent_reindex(entity);
                        String header$ = getEntihome() + "/" + HEADERS + "/" + entity.getKey();
                        try {

                            header = new Sack();
                            header.createElement("label");
                            header.createElement("key");
                            header.setKey(entity.getKey());
                            header.setPath(header$);
                            header.putElementItem("label",
                                    new Core(entity.getAttributeAt("icon"), label$, entity.getKey()));
                            header.putElementItem("key",
                                    new Core(label$, entity.getKey(), entity.getProperty("entity")));
                            header.putAttribute(
                                    new Core(null, TIMESTAMP, String.valueOf(System.currentTimeMillis())));
                            header.saveXML(header$);
                        } catch (Exception e) {
                            //System.out.println("Entigrator:ent_reindex:"+e.toString());
                            //   LOGGER.info(":makeQuickMap:"+e.toString());
                        }
                        save(entity);
                    }
                    quickMap.putElementItem("label", header.getElementItem("label", label$));
                    quickMap.putElementItem("key", key);
                } catch (Exception e) {
                    LOGGER.info(":makeQuickMap:" + e.toString());
                }
            }
        }

        try {
            //long currentTime=System.currentTimeMillis();
            quickMap.putAttribute(new Core(null, TIMESTAMP, String.valueOf(currentTime)));
            new File(getEntihome() + "/" + QUICK_MAP).delete();
            quickMap.saveXML(getEntihome() + "/" + QUICK_MAP);
        } catch (Exception e) {
            LOGGER.severe(":makeQuickMap:" + e.toString());
        }
    }

    private static String[] intersect(String[] list1, String[] list2) {

        if (list2 == null || list1 == null) {
            return null;
        }
        Stack<String> s1 = new Stack<String>();
        Stack<String> s2 = new Stack<String>();
        for (String aList2 : list2)
            s2.push(aList2);
        String line$;
        boolean found;
        String member$ = null;
        while (!s2.isEmpty()) {
            try {
                found = false;
                line$ = s2.pop().toString();
                if (line$ == null)
                    continue;
                for (String aList1 : list1) {
                    member$ = aList1;

                    if (line$.equals(member$)) {
                        found = true;
                        break;
                    }
                }
                if (found)
                    Support.addItem(member$, s1);
                //}
            } catch (Exception e) {
                Logger.getLogger(Entigrator.class.getName()).info(":intersect:" + e.toString());
            }
        }
        int cnt = s1.size();
        if (cnt < 1)
            return new String[0];
        String[] res = new String[cnt];
        for (int i = 0; i < cnt; i++)
            res[i] = s1.pop().toString();
        return res;
    }

    /**
     * Get the icon of the entity encoded into
     * Base64 string 
     *  @param entity the entity.
     * @return the icon string .
     */
    public String readEntityIcon(Sack entity) {
        String iconString$ = Support.readHandlerIcon(this, getClass(), "box.png");
        try {
            String icon$ = entity.getAttributeAt("icon");
            if (icon$ == null)
                return iconString$;

            String path$ = getEntihome() + "/" + ICONS + "/" + icon$;

            FileInputStream is = new FileInputStream(path$);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int bytesRead = 0;
            while ((bytesRead = is.read(b)) != -1) {
                bos.write(b, 0, bytesRead);
            }
            byte[] ba = bos.toByteArray();
            is.close();
            return Base64.encodeBase64String(ba);
        } catch (Exception e) {
            //Logger.getLogger(Entigrator.class.getName()).severe(e.toString());

        }
        return iconString$;
    }

    /**
     * Get the icon from the icon directory
     * encoded as Base64 string. 
     *  @param icon$ the name of icon file.
     * @return the icon string .
     */
    public String readIconFromIcons(String icon$) {
        try {
            String path$ = getEntihome() + "/" + ICONS + "/" + icon$;
            FileInputStream is = new FileInputStream(path$);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int bytesRead = 0;
            while ((bytesRead = is.read(b)) != -1) {
                bos.write(b, 0, bytesRead);
            }
            byte[] ba = bos.toByteArray();
            is.close();
            return Base64.encodeBase64String(ba);
        } catch (Exception e) {

        }
        return null;
    }

    /**
     * Copy the icon from the class resource
     *  into icons folder.
     *  @param handler the handler class
     *  @param icon$ the name of icon resource.
     */
    public void saveHandlerIcon(Class<?> handler, String icon$) {
        try {
            File iconFile = new File(getEntihome() + "/" + ICONS + "/" + icon$);
            if (iconFile.exists())
                return;
            InputStream is = handler.getResourceAsStream(icon$);
            iconFile.createNewFile();
            FileOutputStream fos = new FileOutputStream(iconFile);
            byte[] b = new byte[1024];
            int bytesRead = 0;
            while ((bytesRead = is.read(b)) != -1) {
                fos.write(b, 0, bytesRead);
            }
            is.close();
            fos.close();
        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).severe(e.toString());
        }

    }

    /**
     * Put locator into the locators cache
     *  @param key$ the key string
     *  @param locator$ the locator.
     */
    public void putLocator(String key$, String locator$) {
        if (locatorsCache == null)
            locatorsCache = new Properties();
        locatorsCache.setProperty(key$, locator$);
    }

    /**
     * Get locator from the locators cache
     *  @param key$ the key string
     *  @return the locator.
     */
    public String getLocator(String key$) {
        if (locatorsCache == null)
            return null;
        return locatorsCache.getProperty(key$);
    }

    /**
     * Put class into the classes cache
     *  @param key$ the key string
     *  @param cls the class.
     */
    public void putClass(String key$, Class<?> cls) {
        if (classesCache == null)
            classesCache = new Hashtable<String, Class<?>>();
        classesCache.put(key$, cls);
    }

    /**
     * Get class from the classes cache
     *  @param key$ the key string
     *  @return the class.
     */
    public Class<?> getClass(String key$) {
        if (classesCache == null)
            return null;
        return (Class<?>) Support.getValue(key$, classesCache);
    }
}