All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.genexus.db.odata.ODataClientHelper Maven / Gradle / Ivy

Go to download

Core classes for the runtime used by Java and Android apps generated with GeneXus

The newest version!
package com.genexus.db.odata;

import com.genexus.GXutil;
import com.genexus.db.service.ServiceError;
import com.genexus.db.service.ServiceException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.UUID;
import org.apache.olingo.client.api.domain.ClientCollectionValue;
import org.apache.olingo.client.api.domain.ClientComplexValue;
import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientObjectFactory;
import org.apache.olingo.client.api.domain.ClientPrimitiveValue;
import org.apache.olingo.client.api.domain.ClientProperty;
import org.apache.olingo.client.api.domain.ClientValue;
import org.apache.olingo.client.api.uri.FilterArg;
import org.apache.olingo.client.api.uri.FilterArgFactory;
import org.apache.olingo.client.api.uri.FilterFactory;
import org.apache.olingo.client.api.uri.QueryOption;
import org.apache.olingo.client.api.uri.URIBuilder;
import org.apache.olingo.client.api.uri.URIFilter;
import org.apache.olingo.client.core.uri.FilterLiteral;
import org.apache.olingo.commons.api.edm.EdmComplexType;
import org.apache.olingo.commons.api.edm.EdmElement;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmEnumType;
import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.geo.Geospatial;
import org.apache.olingo.commons.api.edm.geo.Point;
import org.apache.olingo.commons.api.edm.geo.SRID;
import org.apache.olingo.commons.core.Encoder;
import org.apache.olingo.commons.core.edm.primitivetype.EdmGeographyPoint;
import org.apache.olingo.commons.core.edm.primitivetype.EdmString;

public class ODataClientHelper
{
    protected ODataClientHelper(ODataConnection con, ClientEntity[] oEntity, EdmEntityType[] oEntityType)
    {
        this.con = con;
        this.oEntity = oEntity;
        this.oEntityType = oEntityType;
        this.builder = newURIBuilder();        
    }
    
    final List entities = new ArrayList<>();
    final HashMap usedNavProps = new HashMap<>();
    final HashMap> filters = new HashMap<>();
    ClientEntity [] oEntity;
    final EdmEntityType[] oEntityType;
    EdmEntityType baseType;
    String baseEntitySet;
    final List expands = new ArrayList<>();
    final List constrainedProps = new ArrayList<>();
    boolean replaceFilterWithKey;
    boolean isCurrentOfUpdate;
    
    public ODataClientHelper get(String entity, String... select)
    {
		baseEntitySet = entity;
        builder = builder.appendEntitySetSegment(entity);
        if(select.length != 0)
            builder = builder.select(select);
        EdmEntityType entityType = con.getModel().getEntityContainer().getEntitySet(entity).getEntityType();
        entities.add(entityType);
        baseType = entityType; 
        if(oEntity != null)
        {
            replaceFilterWithKey = true; // Aviso que quiero armar una SegmentKey
            if(oEntity[0] == null)
                oEntity[0] = con.client.getObjectFactory().newEntity(entityType.getFullQualifiedName()); 
        }
        if(oEntityType != null)
            oEntityType[0] = entityType;
        return this;
    }    
    
    public ODataClientHelper expand(String entity, String... select)
    {
        String entityName = con.entity(baseType, entity);
        if(entityName == null)
        {
            for(EdmEntityType entityType:entities)
            {
                String mappedEntity = con.entity(entityType, entity);
                if(mappedEntity != null)
                    entityName = mappedEntity;
                EdmNavigationProperty navProp = entityType.getNavigationProperty(entityName);
                if(navProp != null)
                {
                    entities.add(navProp.getType());
                    usedNavProps.put(entityName, navProp);
                    break;
                }
            }
        }else
        {
            EdmNavigationProperty navProp = baseType.getNavigationProperty(entityName);
                if(navProp != null)
                {
                    entities.add(navProp.getType());
                    usedNavProps.put(entityName, navProp);
                }                
        }
        if(entityName == null)
            entityName = entity;
        expands.add(new ExpandItem(entity, entityName, select));
        return this;
    }
    
    public List getConstrainedProps()
    {
        return constrainedProps;
    }
    
// Actualizaciones
    
    private EdmEntityType getEntityType(ClientEntity entity)
    {
        return con.getModel().getEntityType(entity.getTypeName());
    }
    
    public ODataClientHelper set(String key, Object value)
    {
        if(oEntity == null)
            return this;
        ClientEntity entity = getUpdateEntity();
        ClientProperty prop = entity.getProperty(key);
        if(prop == null || prop.hasPrimitiveValue() || prop.hasEnumValue())
        {
            List props = entity.getProperties();
            if(prop != null)
                props.remove(prop);
            ClientObjectFactory objFactory = getObjFactory();
            props.add(newProperty(objFactory, key, value, getEntityType(entity).getProperty(key).getType()));
        }else
        {
            if(prop.hasCollectionValue())
            {
                if(!prop.getCollectionValue().asJavaCollection().contains(value))
                {
                    ClientPrimitiveValue.Builder builder = getObjFactory().newPrimitiveValueBuilder();
                    builder.setValue(value);
                    builder.setType(getEntityType(entity).getProperty(key).getType());
                    prop.getCollectionValue().add(builder.build());
                }else throw new ServiceException(ServiceError.DUPLICATE_KEY.toString(), ServiceError.DUPLICATE_KEY.getSqlState(), ServiceError.DUPLICATE_KEY.getCode());
            }
        }        
        return this;
    }
    
    public ODataClientHelper set(ComplexHashMap complex)
    {
        ClientEntity entity;
        if(complex instanceof CurrentOf)
        {
            CurrentOf currentOf = (CurrentOf)complex;
            entity = currentOf.getUpdateEntity();
            oEntity[0] = entity;
            removeComplex(entity, currentOf.getCurrentOfEntry(), complex.getEntity(), true);
            isCurrentOfUpdate = true;
        }else 
        {
            if(oEntity == null)
                return this; // Caso que mando un "select" para poder hacer un executeUpdate
            entity = getUpdateEntity();
        }
        // Puede ser una propiedad estructurada o una navigation property
        String key = complex.getEntity();
        EdmEntityType entityType = getEntityType(entity);
        EdmElement complexTypeProp = entityType.getStructuralProperty(key);
        if(complexTypeProp != null)
            setComplexProperty(entity, complexTypeProp, (EdmComplexType)complexTypeProp.getType(), complex, key);
        else
        {
            EdmNavigationProperty navProp = entityType.getNavigationProperty(key);
            if(navProp != null)
            {
                setLink(entity, navProp.getType(), complex, key);
            }
        }
        return this;
    }
    
    public ODataClientHelper link(ComplexHashMap complex)
    {
        EdmEntityType entityType = getEntityType(getUpdateEntity());
        linkEntity = complex.getEntity();
        builder = builder.appendEntitySetSegment(linkEntity);
        if(!complex.values().contains(null))
        { // Si es link
            isLink = true;
            builder = builder.appendKeySegment(complex);
        }
        else isLink = false;  // Si hay algun nulo es unlink
        linkEntity = con.entity(entityType, complex.getEntity());
        return this;
    }    
    
    private void setLink(ClientEntity entity, EdmEntityType linkEntityType, ComplexHashMap complex, String key)
    {
        ClientObjectFactory objFactory = getObjFactory();
        
//        URI link = newURIBuilder().appendEntityIdSegment(linkEntityType.getName()).appendKeySegment(complex).build();
        URI link = newURIBuilder().appendEntitySetSegment(linkEntityType.getName()).appendKeySegment(complex).build();
        entity.addLink(objFactory.newEntityNavigationLink(key, link));        
    }
    
    private void setComplexProperty(ClientEntity entity, EdmElement complexTypeProp, EdmComplexType complexType, ComplexHashMap complex, String key)
    {
        ClientComplexValue value;
        boolean isCollection = complexTypeProp.isCollection();
        ClientObjectFactory objFactory = getObjFactory();
        ClientProperty prop = entity.getProperty(key);
        if(prop == null)
        {
            String complexTypeName = complexType.getFullQualifiedName().getFullQualifiedNameAsString();
            value = objFactory.newComplexValue(complexTypeName);
            if(isCollection)
            {
                ClientCollectionValue colValue = objFactory.newCollectionValue(complexTypeName);
                objFactory.newCollectionProperty(complexTypeName, colValue);
                entity.getProperties().add(objFactory.newCollectionProperty(key, colValue));
                colValue.add(value);
            }else
            {            
                entity.getProperties().add(newProperty(objFactory, key, value, complexType));
            }
            fillClientValue(objFactory, complexType, value, complex);
        }else
        { 
            if(prop.hasCollectionValue())
            {// Puede ser una collection de estructurados
                value = objFactory.newComplexValue(complexType.getFullQualifiedName().getFullQualifiedNameAsString());
                fillClientValue(objFactory, complexType, value, complex);
                if(!prop.getCollectionValue().asJavaCollection().contains(value.asJavaMap()))
                    prop.getCollectionValue().add(value);
                else throw new ServiceException(ServiceError.DUPLICATE_KEY.toString(), ServiceError.DUPLICATE_KEY.getSqlState(), ServiceError.DUPLICATE_KEY.getCode());
            }else
            {
                fillClientValue(objFactory, complexType, prop.getComplexValue(), complex);
            }
        }        
    }
    
    public ODataClientHelper remove(ComplexHashMap complex)
    {
        ClientEntity entity = getUpdateEntity();
        removeComplex(entity, complex, complex.getEntity(), false);
        return this;
    }
    
    private boolean removeComplex(ClientEntity entity, HashMap complex, String key, boolean fullCheck)
    {
        ClientComplexValue value;
        EdmElement complexTypeProp = getEntityType(entity).getProperty(key);
        EdmComplexType complexType = (EdmComplexType)complexTypeProp.getType();
        ClientObjectFactory objFactory = getObjFactory();
        ClientProperty prop = entity.getProperty(key);
        boolean found = false;
        if(prop != null)
        { 
            if(prop.hasCollectionValue())
            {
                ClientCollectionValue col = prop.getCollectionValue();
                if(!col.isEmpty())
                {
                    String complexTypeName = complexType.getFullQualifiedName().getFullQualifiedNameAsString();
                    value = objFactory.newComplexValue(complexTypeName);
                    fillClientValue(objFactory, complexType, value, complex);
                    Map valueMap = value.asJavaMap();
                    if(containsValue(col.asJavaCollection(), valueMap, fullCheck))
                    {
                        found = true;
                        ClientCollectionValue colValue = objFactory.newCollectionValue(complexTypeName);
                        entity.getProperties().add(objFactory.newCollectionProperty(key, colValue));
                        for(ClientValue item:col)
                        {
                            if(!equalMap(item.asComplex().asJavaMap(), valueMap, fullCheck))
                            {
                                colValue.add(item);
                            }
                        }
                    }
                }
            }
            entity.getProperties().remove(prop);
        }
        return found;
    }

    private boolean containsValue(Collection col, Map valueMap, boolean fullCheck)
    {
        for(Object item:col)
        {
            if(item instanceof Map)
            {
                @SuppressWarnings("unchecked")
                Map itemAsMap = (Map)item;
                if(equalMap(itemAsMap, valueMap, fullCheck))
                    return true;
            }
        }
        return false;
    }

    private boolean equalMap(Map itemMap, Map valueMap, boolean fullCheck)
    {
        if((fullCheck && itemMap.size() != valueMap.size()) ||
           (!fullCheck && itemMap.size() < valueMap.size()))
            return false;
        for(Map.Entry item:itemMap.entrySet())
        {
            String itemKey = item.getKey();
            Object itemValue = item.getValue();
            Object valueMapValue = valueMap.get(itemKey);
            if(itemValue != null && valueMapValue == null && fullCheck)
                return false;
            else if(itemValue != null && valueMapValue != null)
            {
                if(itemValue instanceof Map && valueMapValue instanceof Map)
                {
		                @SuppressWarnings("unchecked")
		                Map itemValueAsMap = (Map)itemValue;
		                @SuppressWarnings("unchecked")
		                Map valueMapValueAsMap = (Map)valueMapValue;
                    if(!equalMap(itemValueAsMap, valueMapValueAsMap, fullCheck))
                        return false;
                }
                else if(!itemValue.toString().equals(valueMapValue.toString()))
                    return false;
            }
        }
        return true;        
    }

    private void fillClientValue(ClientObjectFactory objFactory, EdmComplexType complexType, ClientComplexValue value, HashMap complex)
    {
        for(Map.Entry item:complex.entrySet())
        {
            String key = item.getKey();
            Object memberValue = item.getValue();
            if(memberValue instanceof HashMap)
            {
                @SuppressWarnings("unchecked")
                HashMap childValue = (HashMap)memberValue;
                EdmComplexType childComplexType = (EdmComplexType)complexType.getProperty(key).getType();
                ClientProperty childProp = value.get(key);
                if(childProp == null)
                {
                    childProp = newProperty(objFactory, key, objFactory.newComplexValue(childComplexType.getFullQualifiedName().getFullQualifiedNameAsString()), childComplexType);
                    value.add(childProp);
                }
                fillClientValue(objFactory, childComplexType, childProp.getComplexValue(), childValue);
            }else
            {
                value.add(newProperty(objFactory, key, memberValue, complexType.getStructuralProperty(key).getType()));
            }
        }
    }    
    
    public ODataClientHelper remove(String key, Object value)
    {
        ClientEntity entity = getUpdateEntity();
        ClientProperty prop = entity.getProperty(key);
        List props = entity.getProperties();
        if(prop != null && prop.hasCollectionValue())
            removeClientCollectionValue(props, prop, key, value);
        else
        {
            ClientObjectFactory objFactory = getObjFactory();
            props.remove(newProperty(objFactory, key, value, getEntityType(entity).getProperty(key).getType()));
        }
        
        return this;
    }
    
    private boolean removeClientCollectionValue(List props, ClientProperty prop, String key, Object value)
    {
        ClientCollectionValue values = prop.getValue().asCollection();        
        ClientObjectFactory objFactory = getObjFactory();
        boolean found = false;
        ClientCollectionValue newValues = objFactory.newCollectionValue(prop.getValue().getTypeName());
        for(Object itemObj : values)
        {
            if(itemObj instanceof ClientValue)
            {
                ClientValue item = (ClientValue)itemObj;
                if(value.equals(item.asPrimitive().toValue()))
                {
                    found = true;
                }else
                {
                    newValues.add(item);                        
                }
            }
        }
        if(found)
        {
            props.remove(prop); 
            props.add(objFactory.newCollectionProperty(key, newValues));
        }
        return found;
    }
        
    Map keySegment;
    public ODataClientHelper key()
    {
        ClientEntity entity = getUpdateEntity();
        keySegment = new HashMap<>();
        
        for(EdmKeyPropertyRef keyRef : baseType.getKeyPropertyRefs())
        {
            keySegment.put(keyRef.getName(), getPropertyValue(entity.getProperty(keyRef.getName())));
        }
        entity.getNavigationLinks().clear();
        return this;
    }
    
    private boolean isLink, hasKeySegment;
    private String linkEntity = null;
    public ODataClientHelper keyRef(String linkEntity)
    {
        this.linkEntity = linkEntity;
        return key();
    }
    
    public String getLinkEntity()
    {
        return linkEntity;
    }
    
    public boolean isLink()
    {
        return isLink;
    }
    
    public boolean hasKeySegment()
    {
        return hasKeySegment;
    }    
    
    private ClientEntity getUpdateEntity()
    {
        return oEntity[0];
    }
    
    public boolean isCurrentOfUpdate()
    {
        return isCurrentOfUpdate;
    }    
    
    private boolean setsNullToNonStringType = false;
    public boolean getSetsNullToNonStringType()
    {
        //@hack: Olingo 4.5 no esta armando bien el Json de un PATCH cuando se manda un campo con null (la metadata full indica que el tipo de dato es String)
        //https://issues.apache.org/jira/browse/OLINGO-1114
        return setsNullToNonStringType;
    }    
    
    private Object getPropertyValue(ClientProperty prop)
    {
        ClientValue clientValue = prop.getValue();
        if(clientValue.isPrimitive())
            return clientValue.asPrimitive().toValue();
        else if(clientValue.isEnum())
            return clientValue.asEnum().getValue();
        else if(clientValue.isComplex())
            return clientValue.asComplex().asJavaMap();
        else if(clientValue.isCollection())
            return clientValue.asCollection().asJavaCollection();
        else return null;
    }
    
    
// Fin actualizaciones

// Consultas    
    public ODataClientHelper orderBy(String name)
    {
        return orderBy(null, name);
    }    
    
    public ODataClientHelper orderBy(String entity, String name)
    {
        String entityAndName = getEntityAndName(entity, name);
        if(entityAndName == null)
            return this;
        order = order == null ? entityAndName : String.format("%s, %s", order, entityAndName);
        return this;
    }    
    
    public ODataClientHelper orderByDesc(String name)
    {
        return orderByDesc(null, name);
    }    

    public ODataClientHelper orderByDesc(String entity, String name)
    {
        orderBy(entity, name);
        order = String.format("%s desc", order);
        return this;
    }    

    public ODataClientHelper thenBy(String name)
    {
        return thenBy(null, name);
    }    
    
    public ODataClientHelper thenBy(String entity, String name)
    {
        return orderBy(entity, name);
    }    

    public ODataClientHelper thenByDesc(String name)
    {
        return thenByDesc(null, name);
    }    
    
    public ODataClientHelper thenByDesc(String entity, String name)
    {
        return orderByDesc(entity, name);
    }
    
    public ODataClientHelper filter(GxURIFilter filter)
    {
        try
        {
            String currentFilterEntity;
            if(currentFilterEntities == null)
                currentFilterEntity = null;
            else if (currentFilterEntities.size() == 1)
                currentFilterEntity = currentFilterEntities.iterator().next();
            else
            {
                // El filtro va sobre mas de una entidad, veo si puedo partirla en más de una condición
                if(splitInvalidFilter(filter))
                    return this;
                if(currentFilterEntities.remove(null))
                    currentFilterEntities.add("");
                String [] entities = new String[currentFilterEntities.size()];            
                throw new UnsupportedOperationException(String.format("Condition cannot be evaluated: %s because it accesses members from two or more entities (%s)", filter.build(), String.join(", ", currentFilterEntities.toArray(entities))));
            }
            addFilter(currentFilterEntity, filter);
            return this;        
        }finally
        {
            currentFilterEntities = null;            
        }
    }
    
    private void addFilter(String currentFilterEntity, GxURIFilter filter)
    {
		List currentFilters = filters.computeIfAbsent(currentFilterEntity, k -> new ArrayList<>());
		currentFilters.add(filter);
    }
    
    private boolean splitInvalidFilter(GxURIFilter filter)
    {
        Stack filtersToProcess = new Stack<>();
        Map okFilters = new HashMap<>();
        filtersToProcess.add(filter);
        while(!filtersToProcess.empty())
        {
            filter = filtersToProcess.pop();
            Set entitiesSet = new HashSet<>();
            if(filter.fillEntitiesSet(entitiesSet).size() > 1)
            {
                if(filter.op == GxURIOp.AND)
                {
                    GxURIFilterLogical andFilter = (GxURIFilterLogical)filter;
                    filtersToProcess.add(andFilter.right);
                    filtersToProcess.add(andFilter.left);
                    continue;
                }else if(filter.op == GxURIOp.NOT)
                {
                    boolean notParity = false;
                    GxURIFilter notFilter;
                    for(notFilter = ((GxURIFilterLogicalUnary)filter).left; notFilter.op == GxURIOp.NOT; notFilter = ((GxURIFilterLogicalUnary)notFilter).left)
                        notParity = !notParity;
                    if(notParity)
                    {
                        filtersToProcess.add(notFilter);
                        continue;
                    }
                    if(notFilter.op == GxURIOp.OR)
                    { // DeMorgan
                        GxURIFilterLogical orFilter = (GxURIFilterLogical)notFilter;
                        filtersToProcess.add(not(orFilter.right));
                        filtersToProcess.add(not(orFilter.left));
                        continue;
                    }
                }
                return false; // No pude partir el filtro
            }else
            {
                okFilters.put(filter, entitiesSet.isEmpty() ? null : entitiesSet.iterator().next());
            }
        }
        for(Map.Entry item:okFilters.entrySet())
        {
            GxURIFilter okFilter = item.getKey();
            addFilter(item.getValue(), okFilter);
        }
        return true;
    }

    public ODataConnection con;
    private URIBuilder builder;
    private String order = null;
    public URI build(ODataQuery query, Object[] parms)
    {
        try
        {
            if(builder == null)
                builder = newURIBuilder();
            if(query != null)
                query.query.apply(this, parms);
            if(order != null)
                builder = builder.orderBy(order);
            for(ExpandItem expand : expands)
            {
                String entity = expand.entity;
                List expandFilters = filters.get(entity);
                if(expandFilters == null)
                    builder = builder.expandWithSelect(expand.entityName, expand.select);
                else
                {
                    EnumMap options = new EnumMap<>(QueryOption.class);
                    options.put(QueryOption.SELECT, expand.getSelect());

                    GxURIFilter filter = null;
                    for(GxURIFilter other:expandFilters)
                    {
                        other = other.simplify();
                        if(other == GxURIFilterBooleanConst.TRUE)
                            continue;
                        filter = (filter == null ? other : and(filter, other));
                    }
                    if(filter != null)
                    {
                        options.put(QueryOption.FILTER, filter.build());
                        constrainedProps.add(expand.entityName);
                    }
//                    options.put(QueryOption.COUNT, true);
                    builder = builder.expandWithOptions(expand.entityName, options);                    
                }
            }
            List baseFilters = filters.get(null);
            if(replaceFilterWithKey)
            {
                if(keySegment == null)
                    keySegment = setKeySegmentFromFilters(baseFilters);
                if(keySegment != null)
                {
                    builder = builder.appendKeySegment(keySegment);
                    if(linkEntity != null)
                    {
                        builder = builder.appendNavigationSegment(linkEntity).appendRefSegment();
                    }
                }
            }
            if(baseFilters != null && !baseFilters.isEmpty())
            {
                GxURIFilter filter = null;
                for(GxURIFilter other:baseFilters)
                {             
                    other = other.simplify();
                    if(other == GxURIFilterBooleanConst.TRUE)
                        continue;
                    if(other.op == GxURIOp.EQ)
                    {
                        // En las condiciones de tipo Att = parmStr le hago un rtrim al parmStr para soportar el caso
			// de tener una FK desde una tabla SQL hacia una entidad OData. En la tabla SQL la FK queda con espacios al final
                        GxURIFilterRelEq equFilter = (GxURIFilterRelEq)other;
                        if(equFilter.left.arg instanceof GxMember &&
                           equFilter.rightObj instanceof String)
                        {
                            equFilter.rightObj = GXutil.rtrim((String)equFilter.rightObj);
                        }
                    }
                    filter = (filter == null ? other : and(filter, other));
                }
                if(filter != null)
                    builder.filter(filter.build());                        
            }
            URI uri = builder.build();
            if(isCurrentOfUpdate)
            {
                URI editUri = oEntity[0].getEditLink();
                if(editUri.getScheme().equals(uri.getScheme()))
                    uri = editUri;
                else
                {
                    try
                    {
                        uri = new URI(uri.getScheme(), editUri.getUserInfo(), editUri.getHost(), editUri.getPort(), editUri.getPath(), editUri.getQuery(), editUri.getFragment());
                    }catch(URISyntaxException e)
                    {
                        uri = editUri;
                    }
                }
            }
            return uri;
        }finally
        {
            hasKeySegment = (keySegment != null);
            con = null;
            builder = null;
            order = null;
            oEntity = null;
            keySegment = null;
        }
    }
    
    private URIBuilder newURIBuilder()
    {
        return con.client.newURIBuilder(con.url);
    }

    private Map setKeySegmentFromFilters(List filters)
    {
        if(filters == null)
            return null;
        List keyFilters = new ArrayList<>();
        Map keySegment = new HashMap<>();
        for(EdmKeyPropertyRef keyRef : baseType.getKeyPropertyRefs())
        {
            GxURIFilter keyFilter = null;
            String keyName = keyRef.getProperty().getName();
            for(GxURIFilter filter:filters)
            {
                if(filter.op == GxURIOp.EQ)
                {
                    GxURIFilterRelEq equFilter = (GxURIFilterRelEq)filter;
                    GxMember equMember = null;
                    Object value = null;
                    if(equFilter.left.arg instanceof GxMember)
                    {
                        equMember = (GxMember)equFilter.left.arg;
                        value = equFilter.rightObj;
                    }
                    else if(equFilter.right.arg instanceof GxMember)
                    {
                        equMember = (GxMember)equFilter.right.arg;
                        value = equFilter.leftObj;
                    }
                    if(equMember != null && keyName.equals(equMember.memberName))
                    {
                        keyFilter = filter;
                        if(value instanceof String)
                            value = Encoder.encode((String)value);
                        keySegment.put(equMember.memberName, value);
                        break;
                    }
                }
            }
            if(keyFilter == null)
                return null; // No pude instanciar la clave
            keyFilters.add(keyFilter);                
        }
        filters.removeAll(keyFilters);
        return keySegment;
    }
    
    
    private ClientProperty newProperty(ClientObjectFactory objFactory, String key, Object value, EdmType type)
    {
        switch(type.getKind())
        {
            case PRIMITIVE:
            {
                ClientPrimitiveValue.Builder builder = objFactory.newPrimitiveValueBuilder();
                builder.setType(type);
                if(value != null && type instanceof EdmGeographyPoint)
                {
                    Point geoPoint;
                    try {
                        geoPoint = EdmGeographyPoint.getInstance().valueOfString(String.format("geography'SRID=0;%s'", value.toString().replace(" (", "(")), null, null, null, null, null, Point.class);
                    } catch (EdmPrimitiveTypeException ex) 
                    {
                        geoPoint = new Point(Geospatial.Dimension.GEOGRAPHY, SRID.valueOf("0"));
                        geoPoint.setX(0);
                        geoPoint.setY(0);
                        geoPoint.setZ(0);
                    }
                    value = geoPoint;
                }
                setsNullToNonStringType |= (value == null && !(type instanceof EdmString)); // @hack para resolver bug Olingo 4.5 con nulos y tipo de dato <> string
                builder.setValue(value);
                return objFactory.newPrimitiveProperty(key, builder.build());
            }
            case ENUM:
            {
                setsNullToNonStringType |= (value == null);
                return objFactory.newEnumProperty(key, objFactory.newEnumValue(type.getFullQualifiedName().getFullQualifiedNameAsString(), value != null ? con.toEnumValue((EdmEnumType)type, (int)value) : null));
            }
            case COMPLEX:
            {
                setsNullToNonStringType |= (value == null);
                return objFactory.newComplexProperty(key, (ClientComplexValue)value);
            }
            default:
                throw new UnsupportedOperationException("Not supported yet : " + value.getClass().toString() + " - " + value.toString());
        }
    }

    private ClientObjectFactory getObjFactory()
    {
        return con.client.getObjectFactory();
    }    
    
    private String getEntityAndName(String entity, String name)
    {
        if(entity != null)
        {
            String entityName = entity;
            for(EdmEntityType entityType:entities)
            {
                String mappedEntity = con.entity(entityType, entity);
                entityName = mappedEntity != null ? mappedEntity : entity;
                EdmNavigationProperty navProp = entityType.getNavigationProperty(entityName);
                if(navProp != null && navProp.isCollection())
                    return null; // Aqui se podria poner el orden parcial dentro de la collection
                else break;
            }                        
            return String.format("%s/%s", entityName, name);
        }
        else return name;
    }
    
    // Filtros
    private FilterFactory getFilterFactory()
    {
        return con.client.getFilterFactory();
    }
    
    private FilterArgFactory getArgFactory()
    {
        return con.client.getFilterFactory().getArgFactory();
    }    
    
    public GxURIFilterLogical and(GxURIFilter left, GxURIFilter right)
    {
        FilterFactory filterFactory = getFilterFactory();
        return logical(GxURIOp.AND, filterFactory.and(left, right), left, right);
    }

    public GxURIFilterLogical or(GxURIFilter left, GxURIFilter right)
    {
        FilterFactory filterFactory = getFilterFactory();
        return logical(GxURIOp.OR, filterFactory.or(left, right), left, right);
    }
    
    public GxURIFilterLogical not(GxURIFilter filter)
    {
        FilterFactory filterFactory = getFilterFactory();
        return new GxURIFilterLogicalUnary(GxURIOp.NOT, filterFactory.not(filter), filter);
    }
        
    public GxFilterArg member(String memberName)
    {
        return member(null, memberName);
    }
    
    private HashSet currentFilterEntities = null;
    public GxFilterArg member(String entity, String memberName)
    {
        if(currentFilterEntities == null)
            currentFilterEntities = new HashSet<>();
        currentFilterEntities.add(entity);
        return new GxFilterArgEntity(new GxMember(getArgFactory(), memberName), entity);
    }
    
    @Deprecated
    public GxFilterArg castEnum(String memberName)
    {
        return castEnum(member(memberName));
    }
    
    public GxFilterArg castEnum(GxFilterArg member)
    {
        FilterArgFactory argFactory = getArgFactory();
        return new GxFilterArgEntity(argFactory.cast(member, argFactory.literal("Edm.String")), null);
    }    
    
    public Object minvalue(String sType)
    {
        switch(sType)
        {
            case "Time": // Chequear en la metadata si se soporta la funcion mindatetime()
            case "Date":
                return getNullDate();
            case "Guid":
                return UUID.fromString("00000000-0000-0000-0000-000000000000");
            default:
                 return new GxFilterArgConst("null");
        }
    }

    public GxFilterArg getFilterArg(FilterArgFactory argFactory, Object arg)
    {
        if(arg instanceof GxFilterArg)
            return (GxFilterArg)arg;
        else if(arg instanceof FilterArg)
            return new GxFilterArg((FilterArg)arg);
        else if(arg instanceof java.sql.Timestamp) // Olingo No esta generando bien las constantes timestamp
        {
            try
            {
                FilterArg literal = argFactory.literal(arg);
                return new GxFilterArgConst(URLDecoder.decode(literal.build(), Charset.defaultCharset().name()), literal instanceof FilterLiteral ? (FilterLiteral)literal : null);
            }catch(UnsupportedEncodingException e)
            {
                return new GxFilterArg(argFactory.literal(arg));
            }
        }
        else return new GxFilterArg(argFactory.literal(arg));
    }
    
    public GxFilterArg getFilterArg(FilterArgFactory argFactory, Object arg, boolean removeNil)
    {
        if(arg instanceof GxFilterArg)
            return (GxFilterArg)arg;
        else if(arg instanceof FilterArg)
            return new GxFilterArg((FilterArg)arg);
        else return new GxFilterArg(argFactory.literal(removeNil ? arg.toString().replaceAll("%", "") : arg));
    }
    
    public GxFilterArg add(Object left, Object right)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg leftFilter = getFilterArg(argFactory, left);
        GxFilterArg rightFilter = getFilterArg(argFactory, right);
        return new GxFilterArgOp(argFactory.add(leftFilter, rightFilter), leftFilter, rightFilter);
    }

    public GxFilterArg sub(Object left, Object right)
    {
        FilterArgFactory argFactory = getArgFactory();        
//Esta mal el codigo del ArgFactory.sub:        return argFactory.sub(getFilterArg(argFactory, left), getFilterArg(argFactory, right));        
        GxFilterArg leftFilter = getFilterArg(argFactory, left);
        GxFilterArg rightFilter = getFilterArg(argFactory, right);
        return new GxFilterArgOp(new FilterOpImpl("sub", leftFilter, rightFilter), leftFilter, rightFilter);
    }

    public GxFilterArg mul(Object left, Object right)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg leftFilter = getFilterArg(argFactory, left);
        GxFilterArg rightFilter = getFilterArg(argFactory, right);
        return new GxFilterArgOp(argFactory.mul(leftFilter, rightFilter), leftFilter, rightFilter);
    }

    public GxFilterArg div(Object left, Object right)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg leftFilter = getFilterArg(argFactory, left);
        GxFilterArg rightFilter = getFilterArg(argFactory, right);
        return new GxFilterArgOp(argFactory.div(leftFilter, rightFilter), leftFilter, rightFilter);
    }
    
    public GxFilterArg concat(Object left, Object right)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg leftFilter = getFilterArg(argFactory, left);
        GxFilterArg rightFilter = getFilterArg(argFactory, right);
        return new GxFilterArgOp(argFactory.concat(leftFilter, rightFilter), leftFilter, rightFilter);
    }
    
    public GxURIFilterRel startswith(Object obj, Object arg)
    {
        FilterFactory filterFactory = getFilterFactory();
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg objFilter = getFilterArg(argFactory, obj);
        GxFilterArg argFilter = getFilterArg(argFactory, arg);
        return new GxURIFilterRelMatch(filterFactory.match(argFactory.startswith(objFilter, argFilter)), objFilter, argFilter);
    }

    public GxURIFilterRel endswith(Object obj, Object arg)
    {
        FilterFactory filterFactory = getFilterFactory();
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg objFilter = getFilterArg(argFactory, obj);
        GxFilterArg argFilter = getFilterArg(argFactory, arg);
        return new GxURIFilterRelMatch(filterFactory.match(argFactory.endswith(objFilter, argFilter)), objFilter, argFilter);
    }

    public GxFilterArg length(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.length(filter));
    }

    public GxFilterArg indexof(Object obj, Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg objFilter = getFilterArg(argFactory, obj);
        GxFilterArg argFilter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.indexof(objFilter, argFilter));
    }

    public GxFilterArg substring(Object obj, Object pos, Object length)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg objFilter = getFilterArg(argFactory, obj);
        GxFilterArg posFilter = getFilterArg(argFactory, pos);
        GxFilterArg lengthFilter = getFilterArg(argFactory, length);
        return new GxFilterArg(argFactory.substring(objFilter, posFilter, lengthFilter));
    }

    public GxFilterArg tolower(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg, true);
        return new GxFilterArg(argFactory.tolower(filter));
    }
    
    public GxFilterArg toupper(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg, true);
        return new GxFilterArg(argFactory.toupper(filter));
    }
    
    public GxFilterArg trim(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.trim(filter));
    }

    public GxFilterArg year(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.year(filter));
    }

    public GxFilterArg month(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.month(filter));
    }

    public GxFilterArg day(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.day(filter));
    }

    public GxFilterArg hour(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.hour(filter));
    }

    public GxFilterArg minute(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.minute(filter));
    }

    public GxFilterArg second(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.second(filter));
    }

    public GxFilterArg milliseconds(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return mul(new GxFilterArg(argFactory.fractionalseconds(filter)), 1000);
    }

    public GxFilterArg floor(Object arg)
    {
        FilterArgFactory argFactory = getArgFactory();
        GxFilterArg filter = getFilterArg(argFactory, arg);
        return new GxFilterArg(argFactory.floor(filter));
    }
    
    private GxURIFilterRel relational(GxURIOp op, URIFilter filter, GxFilterArg left, GxFilterArg right)
    {
        return new GxURIFilterRel(op, filter, left, right);
    }
    
    private GxURIFilterLogical logical(GxURIOp op, URIFilter filter, GxURIFilter left, GxURIFilter right)
    {
        return new GxURIFilterLogical(op, filter, left, right);
    }
    
    public GxURIFilterRel eq(Object left, Object right)
    {
        FilterFactory filterFactory = getFilterFactory();
        FilterArgFactory argFactory = filterFactory.getArgFactory();                
        GxFilterArg leftArg = getFilterArg(argFactory, left);
        GxFilterArg rightArg = getFilterArg(argFactory, right);
        return new GxURIFilterRelEq(GxURIOp.EQ, filterFactory.eq(leftArg, rightArg), leftArg, rightArg, left, right);
    }

    public GxURIFilterRel ne(Object left, Object right)
    {
        FilterFactory filterFactory = getFilterFactory();
        FilterArgFactory argFactory = filterFactory.getArgFactory();
        GxFilterArg leftArg = getFilterArg(argFactory, left);
        GxFilterArg rightArg = getFilterArg(argFactory, right);
        return new GxURIFilterRelEq(GxURIOp.NE, filterFactory.ne(leftArg, rightArg), leftArg, rightArg, left, right);
    }

    public GxURIFilterRel gt(Object left, Object right)
    {
        FilterFactory filterFactory = getFilterFactory();
        FilterArgFactory argFactory = filterFactory.getArgFactory();                
        GxFilterArg leftArg = getFilterArg(argFactory, left);
        GxFilterArg rightArg = getFilterArg(argFactory, right);
        return relational(GxURIOp.GT, filterFactory.gt(leftArg, rightArg), leftArg, rightArg);
    }
    
    public GxURIFilterRel ge(Object left, Object right)
    {
        FilterFactory filterFactory = getFilterFactory();
        FilterArgFactory argFactory = filterFactory.getArgFactory();                
        GxFilterArg leftArg = getFilterArg(argFactory, left);
        GxFilterArg rightArg = getFilterArg(argFactory, right);
        return relational(GxURIOp.GE, filterFactory.ge(leftArg, rightArg), leftArg, rightArg);
    }

    public GxURIFilterRel lt(Object left, Object right)
    {
        FilterFactory filterFactory = getFilterFactory();
        FilterArgFactory argFactory = filterFactory.getArgFactory();                
        GxFilterArg leftArg = getFilterArg(argFactory, left);
        GxFilterArg rightArg = getFilterArg(argFactory, right);
        return relational(GxURIOp.LT, filterFactory.lt(leftArg, rightArg), leftArg, rightArg);
    }

    public GxURIFilterRel le(Object left, Object right)
    {
        FilterFactory filterFactory = getFilterFactory();
        FilterArgFactory argFactory = filterFactory.getArgFactory();                
        GxFilterArg leftArg = getFilterArg(argFactory, left);
        GxFilterArg rightArg = getFilterArg(argFactory, right);
        return relational(GxURIOp.LE, filterFactory.le(leftArg, rightArg), leftArg, rightArg);
    }

    public GxURIFilterRel contains(Object left, Object right)
    {
        FilterFactory filterFactory = getFilterFactory();
        FilterArgFactory argFactory = filterFactory.getArgFactory();
        GxFilterArg leftArg = getFilterArg(argFactory, left, true);
        GxFilterArg rightArg = getFilterArg(argFactory, right, true);
        return new GxURIFilterRelMatch(filterFactory.match(argFactory.contains(leftArg, rightArg)), leftArg, rightArg);
    }
    
    protected static boolean isDefault(GxFilterArg argObj, Object obj)
    {
        if(obj == null)
            return true;
        if(obj instanceof Number)
            return ((Number)obj).doubleValue() == 0;
        else if(obj instanceof String)
            return obj.equals("");
        else return false;
    }

    private static class ExpandItem
    {
        final String entity;
		final String entityName;
        final String [] select;
        public ExpandItem(String entity, String entityName, String [] select)
        {
            this.entity = entity;
            this.entityName = entityName;
            this.select = select;
        }

        private String getSelect()
        {
            return String.join(", ", select);
        }
    }

    private static class FilterOpImpl implements FilterArg
    {

        final String op;
        final FilterArg left;
		final FilterArg right;
        public FilterOpImpl(String op, FilterArg left, FilterArg right)
        {
            this.op = op;
            this.left = left;
            this.right = right;
        }

        @Override
        public String build()
        {
            return '(' + left.build() +
				' ' + op + ' ' +
				right.build() + ')';
        }        
    }
    
    private java.sql.Timestamp getNullDate()
    {
        return new java.sql.Timestamp(GXutil.resetTime(GXutil.nullDate()).getTime());
    }
    
    // Funciones
    public GxURIFilter nullvalue(Object obj)
    {
        Object right;
        if(obj instanceof String)
            right = "";
        else if(obj instanceof Number)
            right = (double) 0;
        else if(obj instanceof java.util.Date)
            right = getNullDate();
        else if(obj instanceof UUID)
            right = UUID.fromString("00000000-0000-0000-0000-000000000000");
        else
        {
            right = null;
        }
        return eq(obj, right);
    }

    public static class GxFilterArg implements FilterArg
    {
        final FilterArg arg;
        public GxFilterArg(FilterArg arg)
        {
            this.arg = arg;
        }

        public Set fillEntitiesSet(Set entitiesSet)
        {
            return entitiesSet;
        }

        @Override
        public String build()
        {
            return arg.build();
        }
    }

    public static class GxFilterArgOp extends GxFilterArg
    {
        final GxFilterArg left;
		final GxFilterArg right;

        private GxFilterArgOp(FilterArg arg, GxFilterArg left, GxFilterArg right)
        {
            super(arg);
            this.left = left;
            this.right = right;
        }

        @Override
        public Set fillEntitiesSet(Set entitiesSet)
        {            
            return left.fillEntitiesSet(right.fillEntitiesSet(entitiesSet));
        }

        @Override
        public String build()
        {
            return arg.build();
        }
    }

    public static class GxFilterArgEntity extends GxFilterArg
    {
        final String entity;
        
        private GxFilterArgEntity(FilterArg arg, String entity)
        {
            super(arg);
            this.entity = entity;
        }

        @Override
        public Set fillEntitiesSet(Set entitiesSet)
        {
            entitiesSet.add(entity);
            return entitiesSet;
        }
    }

    private static class GxFilterArgConst extends GxFilterArg
    {        
        private final String literal;
        private GxFilterArgConst(String literal)
        {
            this(literal, null);
        }
        
        private GxFilterArgConst(String literal, FilterLiteral filterLiteral)
        {
            super(filterLiteral);
            this.literal = literal;
        }

        @Override
        public String build()
        {
            return literal;
        }        
    }

    private static class GxURIFilterBooleanConst extends GxURIFilter
    {
        protected static final GxURIFilterBooleanConst TRUE = new GxURIFilterBooleanConst(true);
        protected static final GxURIFilterBooleanConst FALSE = new GxURIFilterBooleanConst(false);
        
        public static GxURIFilterBooleanConst get(boolean boolConst)
        {
            return boolConst ? TRUE : FALSE;
        }
        
        private final boolean boolConst;
        private GxURIFilterBooleanConst(boolean boolConst)
        {
            super(GxURIOp.CONST_BOOLEAN);
            this.boolConst = boolConst;
        }

        @Override
        public String build()
        {
            return boolConst ? "true" : "false";
        }
        
        @Override
        public boolean isAlwaysTrue()
        {
            return boolConst;
        }
        
        @Override
        public boolean isAlwaysFalse()
        {
            return !boolConst;
        }
    }
    
    public enum GxURIOp
    {
        MATCH,
        EQ,
        NE,
        GT,
        GE,
        LT,
        LE,
        AND,
        OR,
        NOT,
        CONST_BOOLEAN
    }
    
    public static abstract class GxURIFilter implements URIFilter
    {
        final GxURIOp op;
        public GxURIFilter(GxURIOp op)
        {
            this.op = op;
        }

        public Set fillEntitiesSet(Set entitiesSet)
        {
            return entitiesSet;
        }
        
        protected GxURIFilter simplify()
        {
            return this;
        }

        protected boolean isAlwaysTrue()
        {
            return false;
        }

        protected boolean isAlwaysFalse()
        {
            return false;
        }
    }
    
    public static class GxURIFilterRel extends GxURIFilter
    {
        final URIFilter filter;
        final GxFilterArg left;
		final GxFilterArg right;
        public GxURIFilterRel(GxURIOp op, URIFilter filter, GxFilterArg left, GxFilterArg right)
        {
            super(op);
            this.filter = filter;
            this.left = left;
            this.right = right;
        }
        
        @Override
        public Set fillEntitiesSet(Set entitiesSet)
        {            
            return left.fillEntitiesSet(right.fillEntitiesSet(entitiesSet));
        }
        
        @Override
        public String build()
        {
            return filter.build();
        }
    }
    
    public static class GxURIFilterRelMatch extends GxURIFilterRel
    {
        public GxURIFilterRelMatch(URIFilter filter, GxFilterArg left, GxFilterArg right)
        {
            super(GxURIOp.MATCH, filter, left, right);
        }
        
        @Override
        protected GxURIFilter simplify()
        {            
            if(right.arg instanceof FilterLiteral && right.build().equals("''"))
                return GxURIFilterBooleanConst.TRUE;
            else return this;
        }
    }
    
    public static class GxURIFilterRelEq extends GxURIFilterRel
    {
        final Object leftObj;
		Object rightObj;
        public GxURIFilterRelEq(GxURIOp eqOrNEq, URIFilter filter, GxFilterArg left, GxFilterArg right, Object leftObj, Object rightObj)
        {
            super(eqOrNEq, filter, left, right);
            this.leftObj = leftObj;
            this.rightObj = rightObj;
        }
        
        @Override
        protected GxURIFilter simplify()
        {            
            boolean trueForEq = (op == GxURIOp.EQ);
            if(left.arg instanceof FilterLiteral && right.arg instanceof FilterLiteral)
            {
                if(leftObj != null && rightObj != null)
                {
                    if(leftObj instanceof Comparable && rightObj instanceof Comparable)
                        return GxURIFilterBooleanConst.get(trueForEq == GXutil.compare((Comparable)leftObj, "=", (Comparable)rightObj));
                    else return GxURIFilterBooleanConst.get(trueForEq == leftObj.equals(rightObj));
                }
                else return GxURIFilterBooleanConst.get(trueForEq == (leftObj != null ? isDefault(left, leftObj) : isDefault(right, rightObj)));
            }
            return this;
        }
    }

    public static class GxURIFilterLogical extends GxURIFilter
    {
        final URIFilter filter;
        GxURIFilter left, right;

        public GxURIFilterLogical(GxURIOp op, URIFilter filter, GxURIFilter left, GxURIFilter right)
        {
            super(op);
            this.filter = filter;
            this.left = left;
            this.right = right;
        }
        
        @Override
        public Set fillEntitiesSet(Set entitiesSet)
        {            
            return left.fillEntitiesSet(right.fillEntitiesSet(entitiesSet));
        }

        @Override
        public String build()
        {
            return filter.build();
        }
        
        @Override
        protected GxURIFilter simplify()
        {
            if(left != null)
			{
				left = left.simplify();
				if (right != null)
					right = right.simplify();
				else if(op != GxURIOp.NOT)
					return this;
				switch (op)
				{
					case AND:
						if (left.isAlwaysFalse() || right.isAlwaysFalse())
							return GxURIFilterBooleanConst.FALSE;
						if (left.isAlwaysTrue())
							return right.isAlwaysTrue() ? GxURIFilterBooleanConst.TRUE : right;
						else if (right.isAlwaysTrue())
							return left;
						break;
					case OR:
						if (left.isAlwaysTrue() || right.isAlwaysTrue())
							return GxURIFilterBooleanConst.TRUE;
						if (left.isAlwaysFalse())
							return right.isAlwaysFalse() ? GxURIFilterBooleanConst.FALSE : right;
						else if (right.isAlwaysFalse())
							return left;
						break;
					case NOT:
						if (left.isAlwaysFalse())
							return GxURIFilterBooleanConst.TRUE;
						else if (left.isAlwaysTrue())
							return GxURIFilterBooleanConst.FALSE;
						break;
					default:
						break;
				}
			}
            return this;
        }
    }

    private static class GxURIFilterLogicalUnary extends GxURIFilterLogical
    {
        public GxURIFilterLogicalUnary(GxURIOp op, URIFilter filter, GxURIFilter arg)
        {
            super(op, filter, arg, null);
        }        

        @Override
        public Set fillEntitiesSet(Set entitiesSet)
        {            
            return left.fillEntitiesSet(entitiesSet);
        }
    }
    
    private static class GxMember implements FilterArg
    {
        final String memberName;
        final FilterArgFactory argFactory;

        public GxMember(FilterArgFactory argFactory, String memberName)
        {
            this.memberName = memberName.replace('.', '/');
            this.argFactory = argFactory;
        }

        @Override
        public String build()
        {
            return argFactory.property(memberName).build();
        }
    }
    
}