Package reportgen.cores.ejb

Source Code of reportgen.cores.ejb.QueryEntity

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package reportgen.cores.ejb;

import reportgen.cores.ejb.annotations.DefineQueryProperty;
import reportgen.cores.ejb.annotations.DefineQueryEntity;
import reportgen.prototype.entity.QSQLBuilder;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import org.jdom.Element;
import reportgen.prototype.context.Context;
import reportgen.prototype.context.group.ContextGroup;
import reportgen.prototype.entity.QEntity;
import reportgen.prototype.entity.QEntityProperty;
import reportgen.utils.ReportException;
import reportgen.utils.Atom;
import reportgen.utils.XML;

/**
* Объект - сущность, учавствующая в отчете.
* Содержит алиас, однозначно идентифицирующий сущность,
* ссылку на родительскую сущность и характер ее связи с родительской сущностью,
* Кроме того содержит список дочерних сущностей, выбранных для отчета.
* @author axe
*/
class QueryEntity extends QueryObject implements QEntity {

    private static final String ATTR_CLASS = "class";
    private static final String ATTR_IDENT = "ident";
    private static final String LINKAGE = "linkage";

    public static final String TAG = "entity";
    private static final String SUBENTITY = "subentities";

    private final Atom atom;
    private final ContextGroup coreContextGroup;
    private LinkageMode linkage;
    private QueryEntity parent;
    private List<QueryEntity> children = new ArrayList<QueryEntity>();

    /**
     * создает сущность на основе указанного класса
     * Используется при создании корневых сущностей
     * @param cls
     */
    public QueryEntity(Class cls, ContextGroup coreContextGroup) {
        super(cls, null);
        atom = new Atom();
        this.coreContextGroup = coreContextGroup;
    }

    /**
     * создает сущность с заданными названием и описанием.
     * используется при создании сущности на основе поля
     * @param cls
     * @param title
     * @param desc
     * @param fieldName
     * @param linkedLocal
     * @param parent
     */
    private QueryEntity(Class cls, String title, String desc, String identificator,
            LinkageMode backLinkage, QueryEntity linkedEntity,
            ContextGroup coreContextGroup) {
        super(cls, identificator, title, desc);
        this.linkage = backLinkage;
        this.parent = linkedEntity;
        this.atom = new Atom();
        this.coreContextGroup = coreContextGroup;
    }

    /**
     * создает сущность с заданным идентификатором - для создания fwd сущностей
     * @param cls
     * @param fieldName
     */
    private QueryEntity(Class cls, String identificator,
            QueryEntity linkedEntity, ContextGroup coreContextGroup) {
        super(cls, identificator);
        this.parent = linkedEntity;
        this.linkage = LinkageMode.backward;
        this.atom = new Atom();
        this.coreContextGroup = coreContextGroup;
    }


    public QueryEntity(Context context, Element element, ContextGroup coreContextGroup) throws ReportException {
        this(context, element, null, coreContextGroup);
    }

    private QueryEntity(Context context, Element element,
            QueryEntity linkedEntity, ContextGroup coreContextGroup) throws ReportException {
        super(loadClass(XML.getStringAttribute(element, ATTR_CLASS)),
                XML.getStringAttribute(element, ATTR_IDENT, null, false));
        if(!element.getName().equals(TAG)) {
            throw new ReportException("Имя тега не соответствует " + TAG);
        }
        this.coreContextGroup = coreContextGroup;

        atom = new Atom(element, context);
        this.parent = linkedEntity;

        String linkageEl = XML.getStringAttribute(element, LINKAGE, null, false);
        if(linkageEl == null) {
            linkage = LinkageMode.backward;
        } else {
            try {
                linkage = LinkageMode.valueOf(linkageEl);
            } catch (IllegalArgumentException ex) {
                throw new ReportException("Неизвестное значение аттрибута " + LINKAGE);
            }
        }

        Element subentitiesRoot = element.getChild(SUBENTITY);
        if(subentitiesRoot != null) {
            List subentities = subentitiesRoot.getChildren();
            for(int i=0; i<subentities.size(); i++) {
                Element subentity = (Element) subentities.get(i);
                if(!subentity.getName().equals(TAG)) {
                    continue;
                }
                QueryEntity subren = new QueryEntity(context, subentity, this, coreContextGroup);
                if(subren.getLinkage() == null) {
                    throw new ReportException("Отсутствует аттрибут " + LINKAGE);
                }
                children.add(subren);
            }
        }
    }

    private static QueryEntitySet getEntitySet() {
        return CoreFactoryEjb.getEntitySet();
    }

    private static Class loadClass(String name) {
        Class cls = getEntitySet().getClassFromName(name);
        if(cls != null) {
            return cls;
        } else {
            int index = name.lastIndexOf(".");
            if(index != -1) {
                name = name.substring(index+1);
                return loadClass(name);
            }
        }
        throw new RuntimeException("Class '" + name + "' not found!");
    }


    @Override
    public Element toXML() {
        Element root = new Element(TAG);
        atom.toXML(root);
        root.setAttribute(ATTR_CLASS, getCls().getSimpleName());

        String ident = getIdentification();
        if(ident != null) {
            root.setAttribute(ATTR_IDENT, ident);
        }
        if(linkage != null) {
            root.setAttribute(LINKAGE, linkage.toString());
        }
        if(children.size() > 0) {
            Element subent = new Element("subentities");
            for(int i=0; i<children.size(); i++) {
                subent.addContent(children.get(i).toXML());
            }
            root.addContent(subent);
        }
        return root;
    }

    @Override
    public ContextGroup getContextGroup() {
        return coreContextGroup;
    }

    @Override
    public String toString() {
        return super.toString() + "(id:"+ atom + ")";
    }

    /**
     * Возвращает тип связи с линкованной сущностью
     * @return true связан обратной ссылкой с родительской сущностью, false - прямой
     */
    public LinkageMode getLinkage() {
        return linkage;
    }

    @Override
    public boolean isJoined() {
        return linkage != null
                && linkage == LinkageMode.join_forward;
    }

    @Override
    public void setJoined(boolean joined) {
        if(linkage == null) {
            throw new RuntimeException("Данная сущность не может быть связана в режиме JOIN, "
                    + "поскольку является корневой сущностью");
        }
        if(!joined) {
            if(linkage == LinkageMode.join_forward) {
                linkage = LinkageMode.forward;
            } else {
                throw new RuntimeException("Данная сущность не может быть связана в режиме JOIN");
            }
        } else {
            if(linkage == LinkageMode.forward) {
                linkage = LinkageMode.join_forward;
            } else {
                throw new RuntimeException("Данная сущность не может быть связана в режиме JOIN");
            }
        }
    }

    /**
     * Является ли сущность "опциональной" в запросе.
     * Вернет тру если сущность или ее предок заджойнены
     * @return
     */
    @Override
    public boolean isJoinedAnywere() {
        return isJoined()
                || (parent != null && parent.isJoinedAnywere());
    }


    /**
     * не все сущности являются bidirectional
     * Селект для unidirectional в обратном направлении возможен, а джойн - нет
     * @return
     */
    @Override
    public boolean canBeJoined() {
        if(linkage == LinkageMode.backward || getParentEntity() == null) {
            return false;
        }
        return isNullable(getParentEntity().getCls(), getIdentification());
    }

    private static boolean isNullable(Class parentClazz, String ident) {
        try {
            Field f = parentClazz.getDeclaredField(ident);
            Column columnAnnot = f.getAnnotation(Column.class);
            if(columnAnnot == null) {
                JoinColumn jColumnAnnot = f.getAnnotation(JoinColumn.class);
                if(jColumnAnnot == null) {
                    throw new RuntimeException("No column annotation on field '"
                            + ident + "' at " + parentClazz.getName());
                } else if(!jColumnAnnot.nullable()) {
                    return false;
                }
            } else if(!columnAnnot.nullable()) {
                return false;
            }
        } catch (NoSuchFieldException ex) {
            if(parentClazz.getSuperclass() != null) {
                return isNullable(parentClazz.getSuperclass(), ident);
            }
            throw new RuntimeException(ex);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        return true;       
    }

    @Override
    public Atom getAtom() {
        return atom;
    }

    /**
     * Возвращает алиас сущности
     * @return
     */
    @Override
    public String getAlias() {
        return "a" + atom;
    }

    /**
     * Родительская сущность
     * @return
     */
    @Override
    public QueryEntity getParentEntity() {
        return parent;
    }

    /**
     * Выдает список сущностей выбранных пользователем для отчета
     * @return
     */
    @Override
    public QueryEntity[] getSelectedEntities() {
        if(children.size() == 0) {
            return null;
        }
        QueryEntity[] array = new QueryEntity[children.size()];
        children.toArray(array);
        return array;
    }

    @Override
    public void buildFlatList(List<QEntity> ent) {
        ent.add(this);
        for(int i=0; i<children.size(); i++) {
            children.get(i).buildFlatList(ent);
        }
    }

    /**
     * Добавляет сущность в список выбранных сущностей
     * @param child
     */
    @Override
    public void addSelectedEntity(int index) {
        QueryEntity[] lst = getAvialiableEntities();
        if(lst == null || index<0 || index >= lst.length) {
            throw new IllegalArgumentException("Out of bounds:" + index);
        }
        QueryEntity selected = lst[index];
        selected.reReadClassInfo();
        children.add(selected);
    }
   
    /**
     * удаляет сущность из списка выбранных сущностей
     * @param ren
     */
    @Override
    public void removeSelectedEntity(int index) {
        children.remove(index);
    }

    @Override
    public QSQLBuilder getSQLBuilder(Set<QEntityProperty> props) {
        return new QuerySQLBuilder(this, props);
    }


    /**
     * возвращает список связанных сущностей, доступных для выборки
     * включая сущности с обратной связью
     * @return
     */
    @Override
    public QueryEntity[] getAvialiableEntities() {
        return getAvialiableEntities(getCls(), this, coreContextGroup);
    }

    private static QueryEntity[] getAvialiableEntities(Class cls,
            QueryEntity thisEntity, ContextGroup coreContextGroup) {
        LinkedList<QueryEntity> res = new LinkedList<QueryEntity>();
        if(cls == null || cls == Object.class) {
            return null;
        }
        //children entities - LinkageMode.fwd
        Field[] f = cls.getDeclaredFields();
        for(int i=0; i<f.length; i++) {
            DefineQueryProperty gen = f[i].getAnnotation(DefineQueryProperty.class);
            Class c = f[i].getType();
            if(!c.isAnnotationPresent(Entity.class)
                    || gen == null) {
                    continue;
            }
            res.add(new QueryEntity(c, gen.title(), gen.desc(), f[i].getName(),
                    LinkageMode.forward, thisEntity, coreContextGroup));
        }

        //backlinked
        LinkedReportEntity[] linked = getEntitySet().getLinkedClasses(cls);
        if(linked != null) {
            for (int i = 0; i < linked.length; i++) {
                LinkedReportEntity lren = linked[i];
                res.add(new QueryEntity(lren.cls, lren.field, thisEntity, coreContextGroup));
            }
        }

        QueryEntity[] superEntities = getAvialiableEntities(cls.getSuperclass(),
                thisEntity, coreContextGroup);
        if(superEntities != null) {
            for(QueryEntity qes: superEntities) {
                res.add(qes);
            }
        }
        if(res.size() == 0) {
            return null;
        }
        QueryEntity resArray[] = new QueryEntity[res.size()];
        res.toArray(resArray);
        return resArray;
    }

    @Override
    public List<QEntityProperty> getAvialiableProperties() {
        return getAvialiableProperties(getCls(), "", this);
    }

    private List<QEntityProperty> getAvialiableProperties(Class objClass,
            String path, QueryEntity parent) {
        List<QEntityProperty> res = new ArrayList<QEntityProperty>();
        res.addAll(getFields(objClass, path, parent));
        res.addAll(getMethods(objClass, path, parent));
        return res;
    }


    private List<QEntityProperty> getFields(Class objClass, String path, QueryEntity parent) {
        List<QEntityProperty> res = new ArrayList<QEntityProperty>();

        //super class properties
        Class superClass = objClass.getSuperclass();
        if(superClass != null
                && superClass.isAnnotationPresent(DefineQueryEntity.class)) {
            res.addAll(getFields(superClass, path, parent));
        }

        Object[] fields = objClass.getDeclaredFields();

        ArrayList<Field> embedded = new ArrayList<Field>();
        for(int i=0; i<fields.length; i++) {
            Field field = (Field) fields[i];
            DefineQueryProperty gen = field.getAnnotation(DefineQueryProperty.class);
            if(gen == null) {
                continue;
            } else if(gen.embedded()) {
                embedded.add(field);
            }

            QEntityProperty qep = null;
            Class type = field.getType();
            String name = path + field.getName();
            if(type.isAnnotationPresent(Entity.class)) {
                qep = new QueryEntityObjectField(parent, type, gen.title(), gen.desc(), name);
            } else {
                qep = new QueryEntityField(parent, type, gen.title(), gen.desc(), name);
            }
            res.add(qep);
        }

        //embedded properties
        for(int i=0; i<embedded.size(); i++) {
            Field field = embedded.get(i);
            String newPath = path + field.getName() + ".";
            res.addAll(getFields(field.getType(), newPath, parent));
        }
        return res;
    }

    private List<QEntityProperty> getMethods(Class objClass, String path, QueryEntity parent) {
        List<QEntityProperty> res = new ArrayList<QEntityProperty>();

         Object[] methods = objClass.getDeclaredMethods();
        //super class properties
        Class superClass = objClass.getSuperclass();
        if(superClass != null
                && superClass.isAnnotationPresent(DefineQueryEntity.class)) {
            res.addAll(getMethods(superClass, path, parent));
        }

        ArrayList<Method> embedded = new ArrayList<Method>();
        for(int i=0; i<methods.length; i++) {
            Method method = (Method) methods[i];
            DefineQueryProperty gen = method.getAnnotation(DefineQueryProperty.class);
            if(gen == null) {
                continue;
            } else if(gen.embedded()) {
                embedded.add(method);
                continue;
            }

            Class type = method.getReturnType();
            if(type.isAnnotationPresent(Entity.class)) {
                throw new RuntimeException("Аннотирован метод класса " + method.getDeclaringClass()
                    + " представляющий не встроенную сущность " + method.getName());
            }
            QEntityProperty qep =new QueryEntityMethod(parent, type,
                gen.title(), gen.desc(), path + method.getName());
            res.add(qep);
        }

        //embedded properties
        for(int i=0; i<embedded.size(); i++) {
            Method method = embedded.get(i);
            String newPath = path + method.getName() + ".";
            Class cls = method.getReturnType();
            res.addAll(getMethods(cls, newPath, parent));
        }
        return res;
    }


    /**
     * Объект считается тем же, если совпадают
     * - класс,
     * - идентификатор,
     * - тайтл,
     * - описание
     * - алиас
     * @param obj
     * @return
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final QueryEntity other = (QueryEntity) obj;
        if (!this.atom.equals(other.atom)) {
            return false;
        }
        return super.equals(obj);
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 97 * hash + this.atom.hashCode();
        return hash * super.hashCode();
    }
}
TOP

Related Classes of reportgen.cores.ejb.QueryEntity

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.