ofbiz实体引擎(四) ModelReader的作用

时间:2022-05-03
本文章向大家介绍ofbiz实体引擎(四) ModelReader的作用,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
public class ModelReader implements Serializable {

    public static final String module = ModelReader.class.getName();
    private static final UtilCache<String, ModelReader> readers = UtilCache.createUtilCache("entity.ModelReader", 0, 0);

    protected Map<String, ModelEntity> entityCache = null;

    protected int numEntities = 0;
    protected int numViewEntities = 0;
    protected int numFields = 0;
    protected int numRelations = 0;
    protected int numAutoRelations = 0;

    protected String modelName;

    /**实体资源句柄文件集合*/
    protected Collection<ResourceHandler> entityResourceHandlers;

    /**实体资源句柄文件为key,其下面entityName的集合为v*/
    protected Map<ResourceHandler, Collection<String>> resourceHandlerEntities;

    /**entityName为k 实体资源句柄文件 为v*/
    protected Map<String, ResourceHandler> entityResourceHandlerMap;


    /**
     * @author 郑小康
     *
     * 1.根据delegatorName获取对应DelegatorElement标签实例
     *
     * 2.获取Delegator的entity-model-reader属性值
     *
     * 3.根据entity-model-reader属性值获取ModelReader实例
     *
     * 4.如果ModelReader实例为空,则创建其对应的ModelReader,并获取所有实体缓存
     *
     * 5.以entity-model-reader属性值为k ModelReader实例为v存放到readers这个UtilCache中去
     *
     * 6.返回当前ModelReader实例
     * */
    public static ModelReader getModelReader(String delegatorName) throws GenericEntityException {
        DelegatorElement delegatorInfo = EntityConfig.getInstance().getDelegator(delegatorName);

        if (delegatorInfo == null) {
            throw new GenericEntityConfException("Could not find a delegator with the name " + delegatorName);
        }

        String tempModelName = delegatorInfo.getEntityModelReader();
        ModelReader reader = readers.get(tempModelName);

        if (reader == null) {
            reader = new ModelReader(tempModelName);
            // preload caches...
            reader.getEntityCache();
            reader = readers.putIfAbsentAndGet(tempModelName, reader);
        }
        return reader;
    }

    /**
     * @author 郑小康
     *
     * 1.赋值entity-model-reader的属性
     *
     * 2.根据entity-model-reader的属性值获取其对应的EntityModelReader实例,如果为空就抛出异常
     *    原因:其获取的是EntityConfig实例的中的属性,EntityModelReader是在EntityConfig实例化是加载的属性标签的对象,所以没有是肯定有问题的
     *
     * 3.添加entityngine.xml中的句柄属性标签MainResourceHandler实例
     *
     * 4.获取*****-component.xml文件中entity-resource标签类型为model,根据与entity-model-reader的属性值对应reader-name构建ComponentResourceHandler实例添加到entityModelResourceHandlers这个集合
     *
     * 注意:这个构造器主要是给entityResourceHandlers这个集合中添加了当前EntityModelReader 对应entity-resource对应的实例
     *     它是一个私有构造器,通过getModelReader方法来创建对应实例,后续操作在getModelReader这个静态方法中
     * */
    private ModelReader(String modelName) throws GenericEntityException {
        this.modelName = modelName;
        entityResourceHandlers = new LinkedList<ResourceHandler>();
        resourceHandlerEntities = new HashMap<ResourceHandler, Collection<String>>();
        entityResourceHandlerMap = new HashMap<String, ResourceHandler>();

        EntityModelReader entityModelReaderInfo = EntityConfig.getInstance().getEntityModelReader(modelName);

        if (entityModelReaderInfo == null) {
            throw new GenericEntityConfException("Cound not find an entity-model-reader with the name " + modelName);
        }

        // get all of the main resource model stuff, ie specified in the entityengine.xml file
        for (Resource resourceElement : entityModelReaderInfo.getResourceList()) {
            ResourceHandler handler = new MainResourceHandler(EntityConfig.ENTITY_ENGINE_XML_FILENAME, resourceElement.getLoader(), resourceElement.getLocation());
            entityResourceHandlers.add(handler);
        }

        // get all of the component resource model stuff, ie specified in each fadp-component.xml file
        for (ComponentConfig.EntityResourceInfo componentResourceInfo: ComponentConfig.getAllEntityResourceInfos("model")) {
            if (modelName.equals(componentResourceInfo.readerName)) {
                entityResourceHandlers.add(componentResourceInfo.createResourceHandler());
            }
        }
    }

    /**
     * @author  郑小康
     * 1.判断节点元素是否是entity
     *
     * 2.获取entity-name的值
     *
     * 3.获取entity的redefinition属性,这个属性的作用是说明这个实体不能被覆盖,即entity节点元素不能定义两遍
     *   但这仅仅是一个警告,定义了后面的就会覆盖钱买呢
     *
     * 4.获取当前资源句柄文件的实体名集合,为空则实例化一个LinkedList集合
     *   将当前实体名添加到集合
     *
     * 5.以entityName为k entityResourceHandler为v存放在entityResourceHandlerMap,这样做的好处是根据entityName获取其资源句柄文件
     *
     * 6.实体不为空,构造对应的modelEntity或者ModelViewEntity
     *
     * 7.将实体的资源句柄文件路径添加到当前modelEntity
     *
     * 8.返回当前modelEntity
     *
     * 注意:这里只是构造modelEntity,并没有在数据库建表
     * */
    private ModelEntity buildEntity(ResourceHandler entityResourceHandler, Element curEntityElement, int i, ModelInfo def) throws GenericEntityException {
        boolean isEntity = "entity".equals(curEntityElement.getNodeName());
        String entityName = UtilXml.checkEmpty(curEntityElement.getAttribute("entity-name")).intern();

        //获取entity的redefinition属性,这个属性的作用是
        boolean redefinedEntity = "true".equals(curEntityElement.getAttribute("redefinition"));

        //获取当前entityResourceHandler的resourceHandlerEntityNames,里面存放的是这个句柄文件中存在entity,在这里获取的目的是将当前构建的entity的entityName添加进去
        Collection<String> resourceHandlerEntityNames = resourceHandlerEntities.get(entityResourceHandler);
        if (resourceHandlerEntityNames == null) {
            resourceHandlerEntityNames = new LinkedList<String>();
            resourceHandlerEntities.put(entityResourceHandler, resourceHandlerEntityNames);
        }
        resourceHandlerEntityNames.add(entityName);

        //检查缓存中是包含 如果缓存中包含,且它不允许重定义(entity属性中默认是false) 这样就会报一些井盖
        if (entityCache.containsKey(entityName) && !redefinedEntity) {
            Debug.logWarning("实体 " + entityName + " 被再次定义,其将覆盖原有的", module);
            Debug.logWarning("Entity " + entityName + " 被发现在资源句柄文件 " +
                entityResourceHandler + ", 但是已经被定义在 " + entityResourceHandlerMap.get(entityName).toString(), module);
        }

        //以entityName为k entityResourceHandler为v存放在entityResourceHandlerMap,这样做的好处是根据entityName获取其资源句柄文件
        entityResourceHandlerMap.put(entityName, entityResourceHandler);

        //构造对应的modelEntity或者ModelViewEntity
        ModelEntity modelEntity = null;
        if (isEntity) {
            modelEntity = createModelEntity(curEntityElement, null, def);
        } else {
            modelEntity = createModelViewEntity(curEntityElement, null, def);
        }
        //获取句柄资源文件的路径
        String resourceLocation = entityResourceHandler.getLocation();
        try {
            resourceLocation = entityResourceHandler.getURL().toExternalForm();
        } catch (GenericConfigException e) {
            Debug.logError(e, "Could not get resource URL", module);
        }

        //如果modelEntity不为空,将实体的路径注入到modelEntity
        if (modelEntity != null) {
            modelEntity.setLocation(resourceLocation);
            // utilTimer.timerString("  After entityCache.put -- " + i + " --");
            if (isEntity) {
                if (Debug.verboseOn()) Debug.logVerbose("-- [Entity]: #" + i + ": " + entityName, module);
            } else {
                if (Debug.verboseOn()) Debug.logVerbose("-- [ViewEntity]: #" + i + ": " + entityName, module);
            }
        } else {
            Debug.logWarning("-- -- ENTITYGEN ERROR:getModelEntity: Could not create " +
                "entity for entityName: " + entityName, module);
        }
        return modelEntity;
    }

    /**
     * @author 郑小康
     * 1.检查entityCache是否为空,如果不为空直接返回当前缓存,如果为空才向下执行
     *
     * 2.再次检查,避免其他线程在这个过程创建entityCache
     *
     * 3.对类属性进行初始化 numEntities:实体数量   numViewEntities:视图实体数量     numFields:字段数量
     *                    numRelations:        numAutoRelations                numAutoRelations:
     *
     * 4.创建tempViewEntityList<ModelViewEntity>:临时视图模型实体集合  tempExtendEntityElementList<Element> 扩展实体元素
     *
     * 5.遍历所有资源句柄文件,包括entity-model-reader中孩子标签Resource 和对应的组件下entity-resource
     *
     * 6.根据entityResourceHandler的路径,获取其对应的文档的Document实例
     *
     * 7.从首个节点开始,首先构造ModelInfo,获取当前entity的属性
     *
     * 8.节点有三种 entity view-entity extend-entity
     *   如果是实体或者视图实体,调用buildEntity,构造对应的ModelEntity
     *   视图实体:构造后,添加到tempViewEntityList集合
     *   实体:构造后以entityName为k modelEntity为v放入到entityCache
     *
     *   如果是extend-entity,直接将节点元素添加到对应的tempExtendEntityElementList集合
     *
     * 9.从缓存中获取extend-entity的name相同的ModelEntity,然后对这个ModelEntity进行扩展字段,并且其会覆盖原有entity的属性
     *
     * 10.将视图实体添加到对应的成员ModelEntity,这样就可以通过ModelEntity获取其下面所有ModelViewEntity
     *    并将视图以entityName为k ModelViewEntity为v存放到缓存
     *
     * 11.检查出某些视图存在有些成员实体不存在,列举出来,这些ModelViewEntity并没有加到entitycache中
     *
     * 12构建关系,主要是给当前实体添加其存在的关系集合,关系的实体中也添加这个ModelRelation到CopyOnWriteArrayList<ModelRelation> relations
     *   CopyOnWrite容器即写时复制的容器。
     *   读取的时候拷贝一个副本,进行读取
     *   写入的需要加锁,对副本进行写入之后,再将原容器的引用指向新的容器
     *   这样的好处是可以进行并发的读
     *
     * */
    public Map<String, ModelEntity> getEntityCache() throws GenericEntityException {
        if (entityCache == null) { // don't want to block here
            synchronized (ModelReader.class) {
                // must check if null again as one of the blocked threads can still enter
                if (entityCache == null) { // now it's safe
                    numEntities = 0;
                    numViewEntities = 0;
                    numFields = 0;
                    numRelations = 0;
                    numAutoRelations = 0;

                    entityCache = new HashMap<String, ModelEntity>();
                    List<ModelViewEntity> tempViewEntityList = new LinkedList<ModelViewEntity>();
                    List<Element> tempExtendEntityElementList = new LinkedList<Element>();

                    UtilTimer utilTimer = new UtilTimer();

                    for (ResourceHandler entityResourceHandler: entityResourceHandlers) {

                        // utilTimer.timerString("Before getDocument in file " + entityFileName);
                        Document document = null;

                        try {
                            document = entityResourceHandler.getDocument();
                        } catch (GenericConfigException e) {
                            throw new GenericEntityConfException("Error getting document from resource handler 获取entitymodel.xml文件失败", e);
                        }
                        if (document == null) {
                            throw new GenericEntityConfException("Could not get document for " + entityResourceHandler.toString());
                        }

                        // utilTimer.timerString("Before getDocumentElement in " + entityResourceHandler.toString());
                        Element docElement = document.getDocumentElement();

                        if (docElement == null) {
                            return null;
                        }
                        docElement.normalize();
                        Node curChild = docElement.getFirstChild();

                        ModelInfo def = ModelInfo.createFromElements(ModelInfo.DEFAULT, docElement);
                        int i = 0;

                        if (curChild != null) {
                            utilTimer.timerString("Before start of entity loop in " + entityResourceHandler.toString());
                            do {
                                boolean isEntity = "entity".equals(curChild.getNodeName());
                                boolean isViewEntity = "view-entity".equals(curChild.getNodeName());
                                boolean isExtendEntity = "extend-entity".equals(curChild.getNodeName());

                                if ((isEntity || isViewEntity) && curChild.getNodeType() == Node.ELEMENT_NODE) {
                                    i++;
                                    ModelEntity modelEntity = buildEntity(entityResourceHandler, (Element) curChild, i, def);
                                    // put the view entity in a list to get ready for the second pass to populate fields...
                                    if (isViewEntity) {
                                        tempViewEntityList.add((ModelViewEntity) modelEntity);
                                    } else {
                                        entityCache.put(modelEntity.getEntityName(), modelEntity);
                                    }
                                } else if (isExtendEntity && curChild.getNodeType() == Node.ELEMENT_NODE) {
                                    tempExtendEntityElementList.add((Element) curChild);
                                }
                            } while ((curChild = curChild.getNextSibling()) != null);
                        } else {
                            Debug.logWarning("No child nodes found.", module);
                        }
                        utilTimer.timerString("Finished " + entityResourceHandler.toString() + " - Total Entities: " + i + " FINISHED");
                    }

                    //从缓存中获取extend-entity的name相同的ModelEntity,然后对这个ModelEntity进行扩展字段,并且其会覆盖原有entity的属性
                    for (Element extendEntityElement: tempExtendEntityElementList) {
                        String entityName = UtilXml.checkEmpty(extendEntityElement.getAttribute("entity-name"));
                        ModelEntity modelEntity = entityCache.get(entityName);
                        if (modelEntity == null) throw new GenericEntityConfException("Entity to extend does not exist: " + entityName);
                        modelEntity.addExtendEntity(this, extendEntityElement);
                    }

                    //如果视图不为空,获取视图的大小
                    while (!tempViewEntityList.isEmpty()) {
                        int startSize = tempViewEntityList.size();
                        //对视图进行迭代
                        Iterator<ModelViewEntity> mveIt = tempViewEntityList.iterator();
TEMP_VIEW_LOOP:
                        while (mveIt.hasNext()) {
                            ModelViewEntity curViewEntity = mveIt.next();
                            //遍历当前视图的所有ModelMemberEntity(member-entity)成员,如果在缓存中不存在就不执行,存在则继续执行
                            for (ModelViewEntity.ModelMemberEntity mve: curViewEntity.getAllModelMemberEntities()) {
                                if (!entityCache.containsKey(mve.getEntityName())) {
                                    continue TEMP_VIEW_LOOP;
                                }
                            }
                            mveIt.remove();
                            //注入视图实体所有字段
                            curViewEntity.populateFields(this);
                            //加视图实体添加到其下面所有成员实体ModelEntity下,以为这可以根据ModelEntity查询其所有视图实体
                            for (ModelViewEntity.ModelMemberEntity mve: curViewEntity.getAllModelMemberEntities()) {
                                ModelEntity me = entityCache.get(mve.getEntityName());
                                me.addViewEntity(curViewEntity);
                            }
                            entityCache.put(curViewEntity.getEntityName(), curViewEntity);
                        }
                        //这段代码的作用是标识tempViewEntityList集合中的成员是都存在不包含在entityCache
                        if (tempViewEntityList.size() == startSize) {
                            // Oops, the remaining views reference other entities
                            // that can't be found, or they reference other views
                            // that have some reference problem.
                            break;
                        }
                    }

                    //这段代码的作用是在上面遍历tempViewEntityList,有些MemberEntity在缓存中不存在
                    //检查不存在的memberEntity添加到perViewMissingEntities这个SET集合,并将其给输出
                    if (!tempViewEntityList.isEmpty()) {
                        StringBuilder sb = new StringBuilder("View entities reference non-existant members:n");
                        Set<String> allViews = new HashSet<String>();
                        for (ModelViewEntity curViewEntity: tempViewEntityList) {
                            allViews.add(curViewEntity.getEntityName());
                        }
                        for (ModelViewEntity curViewEntity: tempViewEntityList) {
                            Set<String> perViewMissingEntities = new HashSet<String>();
                            Iterator<ModelViewEntity.ModelMemberEntity> mmeIt = curViewEntity.getAllModelMemberEntities().iterator();
                            while (mmeIt.hasNext()) {
                                ModelViewEntity.ModelMemberEntity mme = mmeIt.next();
                                String memberEntityName = mme.getEntityName();
                                if (!entityCache.containsKey(memberEntityName)) {
                                    // this member is not a real entity
                                    // check to see if it is a view
                                    if (!allViews.contains(memberEntityName)) {
                                        // not a view, it's a real missing entity
                                        perViewMissingEntities.add(memberEntityName);
                                    }
                                }
                            }
                            for (String perViewMissingEntity: perViewMissingEntities) {
                                sb.append("t[").append(curViewEntity.getEntityName()).append("] missing member entity [").append(perViewMissingEntity).append("]n");
                            }

                        }
                        throw new GenericEntityConfException(sb.toString());
                    }

                    /**
                     * @author 郑小康
                     * 1.遍历当前ModelReader下所有实体名
                     * 2.获取对应ModelEntity
                     * 3.将其关系进行迭代处理
                     * 4.如果类型是one 或者one-nofk(不是AutoRelation) 获取其关系ModelEntity
                     * 5.将所有key-map的name添加到curEntityKeyFields集合
                     * 6.实例化ModelRelation
                     * 7.如果是自关联,将ModelRealation添加到当前实体
                     *   如果不是在相关实体加入ModelRealation
                     * */
                    TreeSet<String> orderedMessages = new TreeSet<String>();
                    for (String curEntityName: new TreeSet<String>(this.getEntityNames())) {
                        ModelEntity curModelEntity = this.getModelEntity(curEntityName);
                        if (curModelEntity instanceof ModelViewEntity) {
                            // for view-entities auto-create relationships for all member-entity relationships that have all corresponding fields in the view-entity

                        } else {
                            // for entities auto-create many relationships for all type one relationships

                            // just in case we add a new relation to the same entity, keep in a separate list and add them at the end
                            List<ModelRelation> newSameEntityRelations = new LinkedList<ModelRelation>();

                            Iterator<ModelRelation> relationsIter = curModelEntity.getRelationsIterator();
                            while (relationsIter.hasNext()) {
                                ModelRelation modelRelation = relationsIter.next();
                                if (("one".equals(modelRelation.getType()) || "one-nofk".equals(modelRelation.getType())) && !modelRelation.isAutoRelation()) {
                                    ModelEntity relatedEnt = null;
                                    try {
                                    	/** 得到参考的 RelEntityName. */
                                        relatedEnt = this.getModelEntity(modelRelation.getRelEntityName());
                                    } catch (GenericModelException e) {
//                                    	 com.hanlin.fadp.petrescence.datasource.FindMissedEntity.addMissed(modelRelation.getRelEntityName());
                                        throw new GenericModelException("Error getting related entity [" + modelRelation.getRelEntityName() + "] definition from entity [" + curEntityName + "]", e);
                                    }
                                    if (relatedEnt != null) {
                                        // create the new relationship even if one exists so we can show what we are looking for in the info message
                                        // don't do relationship to the same entity, unless title is "Parent", then do a "Child" automatically
                                        String title = modelRelation.getTitle();
                                        if (curModelEntity.getEntityName().equals(relatedEnt.getEntityName()) && "Parent".equals(title)) {
                                            title = "Child";
                                        }
                                        String description = "";
                                        String type = "";
                                        String relEntityName = curModelEntity.getEntityName();
                                        String fkName = "";
                                        ArrayList<ModelKeyMap> keyMaps = new ArrayList<ModelKeyMap>();
                                        boolean isAutoRelation = true;
                                        Set<String> curEntityKeyFields = new HashSet<String>();
                                        for (ModelKeyMap curkm : modelRelation.getKeyMaps()) {
                                            keyMaps.add(new ModelKeyMap(curkm.getRelFieldName(), curkm.getFieldName()));
                                            curEntityKeyFields.add(curkm.getFieldName());
                                        }
                                        keyMaps.trimToSize();
                                        // decide whether it should be one or many by seeing if the key map represents the complete pk of the relEntity
                                        if (curModelEntity.containsAllPkFieldNames(curEntityKeyFields)) {
                                            // always use one-nofk, we don't want auto-fks getting in for these automatic ones
                                            type = "one-nofk";
                                            // to keep it clean, remove any additional keys that aren't part of the PK
                                            List<String> curPkFieldNames = curModelEntity.getPkFieldNames();
                                            Iterator<ModelKeyMap> nrkmIter = keyMaps.iterator();
                                            while (nrkmIter.hasNext()) {
                                                ModelKeyMap nrkm =nrkmIter.next();
                                                String checkField = nrkm.getRelFieldName();
                                                if (!curPkFieldNames.contains(checkField)) {
                                                    nrkmIter.remove();
                                                }
                                            }
                                        } else {
                                            type= "many";
                                        }
                                        ModelRelation newRel = ModelRelation.create(relatedEnt, description, type, title, relEntityName, fkName, keyMaps, isAutoRelation);

                                        ModelRelation existingRelation = relatedEnt.getRelation(title + curModelEntity.getEntityName());
                                        if (existingRelation == null) {
                                            numAutoRelations++;
                                            if (curModelEntity.getEntityName().equals(relatedEnt.getEntityName())) {
                                                newSameEntityRelations.add(newRel);
                                            } else {
                                                relatedEnt.addRelation(newRel);
                                            }
                                        } else {
                                            if (newRel.equals(existingRelation)) {
                                                // don't warn if the target title+entity = current title+entity
                                                if (Debug.infoOn() && !(title + curModelEntity.getEntityName()).equals(modelRelation.getTitle() + modelRelation.getRelEntityName())) {
                                                    //String errorMsg = "Relation already exists to entity [] with title [" + targetTitle + "],from entity []";
                                                    String message = "Entity [" + relatedEnt.getPackageName() + ":" + relatedEnt.getEntityName() + "] already has identical relationship to entity [" +
                                                            curModelEntity.getEntityName() + "] title [" + title + "]; would auto-create: type [" +
                                                            newRel.getType() + "] and fields [" + newRel.keyMapString(",", "") + "]";
                                                    orderedMessages.add(message);
                                                }
                                            } else {
                                                String message = "Existing relationship with the same name, but different specs found from what would be auto-created for Entity [" + relatedEnt.getEntityName() + "] and relationship to entity [" +
                                                        curModelEntity.getEntityName() + "] title [" + title + "]; would auto-create: type [" +
                                                        newRel.getType() + "] and fields [" + newRel.keyMapString(",", "") + "]";
                                                Debug.logVerbose(message, module);
                                            }
                                        }
                                    } else {
                                        String errorMsg = "Could not find related entity ["
                                                + modelRelation.getRelEntityName() + "], no reverse relation added.";
                                        Debug.logWarning(errorMsg, module);
                                    }
                                }
                            }

                            if (newSameEntityRelations.size() > 0) {
                                for (ModelRelation newRel: newSameEntityRelations) {
                                    curModelEntity.addRelation(newRel);
                                }
                            }
                        }
                    }
                    if (Debug.infoOn()) {
                        for (String message : orderedMessages) {
                            Debug.logInfo(message, module);
                        }
                        Debug.logInfo("Finished loading entities; #Entities=" + numEntities + " #ViewEntities=" + numViewEntities + " #Fields=" + numFields + " #Relationships=" + numRelations + " #AutoRelationships=" + numAutoRelations, module);
                    }
                }
            }
        }
        return entityCache;
    }

    /**
     *  rebuilds the resourceHandlerEntities Map of Collections based on the current
     *  entityResourceHandlerMap Map, must be done whenever a manual change is made to the
     *  entityResourceHandlerMap Map after the initial load to make them consistent again.
     *
     *  Map<ResourceHandler, Collection<String>> resourceHandlerEntities
     *  Map<String, ResourceHandler> entityResourceHandlerMap
     *  依据entityResourceHandlerMap来更新resourceHandlerEntities
     *  FIXME:暂时不理解为什么会出现这种情况
     */
    public void rebuildResourceHandlerEntities() {
        resourceHandlerEntities = new HashMap<ResourceHandler, Collection<String>>();
        Iterator<Map.Entry<String, ResourceHandler>> entityResourceIter = entityResourceHandlerMap.entrySet().iterator();

        while (entityResourceIter.hasNext()) {
            Map.Entry<String, ResourceHandler> entry = entityResourceIter.next();
            // add entityName to appropriate resourceHandlerEntities collection
            Collection<String> resourceHandlerEntityNames = resourceHandlerEntities.get(entry.getValue());

            if (resourceHandlerEntityNames == null) {
                resourceHandlerEntityNames = new LinkedList<String>();
                resourceHandlerEntities.put(entry.getValue(), resourceHandlerEntityNames);
            }
            resourceHandlerEntityNames.add(entry.getKey());
        }
    }

    /**获取当前模型阅读器的resourceHandler迭代器*/
    public Iterator<ResourceHandler> getResourceHandlerEntitiesKeyIterator() {
        if (resourceHandlerEntities == null) return null;
        return resourceHandlerEntities.keySet().iterator();
    }

    public Collection<String> getResourceHandlerEntities(ResourceHandler resourceHandler) {
        if (resourceHandlerEntities == null) return null;
        return resourceHandlerEntities.get(resourceHandler);
    }

    public void addEntityToResourceHandler(String entityName, String loaderName, String location) {
        entityResourceHandlerMap.put(entityName, new MainResourceHandler(EntityConfig.ENTITY_ENGINE_XML_FILENAME, loaderName, location));
    }

    public ResourceHandler getEntityResourceHandler(String entityName) {
        return entityResourceHandlerMap.get(entityName);
    }

    /** Gets an Entity object based on a definition from the specified XML Entity descriptor file.
     * @param entityName The entityName of the Entity definition to use.
     * @return An Entity object describing the specified entity of the specified descriptor file.
     */
    public ModelEntity getModelEntity(String entityName) throws GenericEntityException {
        if (entityName == null) {
            throw new IllegalArgumentException("Tried to find entity definition for a null entityName");
        }
        Map<String, ModelEntity> ec = getEntityCache();
        if (ec == null) {
            throw new GenericEntityConfException("ERROR: Unable to load Entity Cache");
        }
        ModelEntity modelEntity = ec.get(entityName);
        if (modelEntity == null) {
            String errMsg = "Could not find definition for entity name " + entityName;
            // Debug.logError(new Exception("Placeholder"), errMsg, module);
//            com.hanlin.fadp.petrescence.datasource.FindMissedEntity.addMissed(entityName);
            throw new GenericModelException(errMsg);
        }
        return modelEntity;
    }

    public ModelEntity getModelEntityNoCheck(String entityName) {
        Map<String, ModelEntity> ec = null;
        try {
            ec = getEntityCache();
        } catch (GenericEntityException e) {
            Debug.logError(e, "Error getting entity cache", module);
        }
        if (ec == null) {
            return null;
        }
        ModelEntity modelEntity = ec.get(entityName);
        return modelEntity;
    }

    /** Creates a Iterator with the entityName of each Entity defined in the specified XML Entity Descriptor file.
     * @return A Iterator of entityName Strings
     */
    public Iterator<String> getEntityNamesIterator() throws GenericEntityException {
        Collection<String> collection = getEntityNames();
        if (collection != null) {
            return collection.iterator();
        } else {
            return null;
        }
    }

    /** Creates a Set with the entityName of each Entity defined in the specified XML Entity Descriptor file.
     * @return A Set of entityName Strings
     */
    public Set<String> getEntityNames() throws GenericEntityException {
        Map<String, ModelEntity> ec = getEntityCache();
        if (ec == null) {
            throw new GenericEntityConfException("ERROR: Unable to load Entity Cache");
        }
        return ec.keySet();
    }

    /** Get all entities, organized by package */
    public Map<String, TreeSet<String>> getEntitiesByPackage(Set<String> packageFilterSet, Set<String> entityFilterSet) throws GenericEntityException {
        Map<String, TreeSet<String>> entitiesByPackage = new HashMap<String, TreeSet<String>>();

        //put the entityNames TreeSets in a HashMap by packageName
        Iterator<String> ecIter = this.getEntityNames().iterator();
        while (ecIter.hasNext()) {
            String entityName = ecIter.next();
            ModelEntity entity = this.getModelEntity(entityName);
            String packageName = entity.getPackageName();

            if (UtilValidate.isNotEmpty(packageFilterSet)) {
                // does it match any of these?
                boolean foundMatch = false;
                for (String packageFilter: packageFilterSet) {
                    if (packageName.contains(packageFilter)) {
                        foundMatch = true;
                    }
                }
                if (!foundMatch) {
                    //Debug.logInfo("Not including entity " + entityName + " becuase it is not in the package set: " + packageFilterSet, module);
                    continue;
                }
            }
            if (UtilValidate.isNotEmpty(entityFilterSet) && !entityFilterSet.contains(entityName)) {
                //Debug.logInfo("Not including entity " + entityName + " because it is not in the entity set: " + entityFilterSet, module);
                continue;
            }

            TreeSet<String> entities = entitiesByPackage.get(entity.getPackageName());
            if (entities == null) {
                entities = new TreeSet<String>();
                entitiesByPackage.put(entity.getPackageName(), entities);
            }
            entities.add(entityName);
        }

        return entitiesByPackage;
    }

    /** Util method to validate an entity name; if no entity is found with the name,
     *  characters are stripped from the beginning of the name until a valid entity name is found.
     *  It is intended to be used to determine the entity name from a relation name.
     *  @return A valid entityName or null
     */
    public String validateEntityName(String entityName) throws GenericEntityException {
        if (entityName == null) {
            return null;
        }
        Set<String> allEntities = this.getEntityNames();
        while (!allEntities.contains(entityName) && entityName.length() > 0) {
            entityName = entityName.substring(1);
        }
        return (entityName.length() > 0? entityName: null);
    }

    ModelEntity createModelEntity(Element entityElement, UtilTimer utilTimer, ModelInfo def) {
        if (entityElement == null) return null;
        this.numEntities++;
        ModelEntity entity = new ModelEntity(this, entityElement, utilTimer, def);
        return entity;
    }

    ModelEntity createModelViewEntity(Element entityElement, UtilTimer utilTimer, ModelInfo def) {
        if (entityElement == null) return null;
        this.numViewEntities++;
        ModelViewEntity entity = new ModelViewEntity(this, entityElement, utilTimer, def);
        return entity;
    }


    public ModelRelation createRelation(ModelEntity entity, Element relationElement) {
        this.numRelations++;
        ModelRelation relation = ModelRelation.create(entity, relationElement, false);
        return relation;
    }

    /**增加当前ModelReader字段个数*/
    public void incrementFieldCount(int amount) {
        this.numFields += amount;
    }