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

com.genexus.db.odata.ODataResultSet 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.DebugFlag;
import com.genexus.db.driver.GXDBDebug;
import com.genexus.db.service.*;
import java.io.*;
import java.net.*;
import java.sql.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.logging.*;
import org.apache.olingo.client.api.*;
import org.apache.olingo.client.api.communication.ODataServerErrorException;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
import org.apache.olingo.client.api.domain.*;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;

class ODataResultSet extends ServiceResultSet
{
    private ODataPreparedStatement stmt;
    private boolean skipBaseProperties = true;
    private EdmEntityType baseEntityType = null;
    private boolean trackCurrentOf;
    private HashSet allSelectedKeys = null;

    public ODataResultSet(ODataPreparedStatement stmt) throws SQLException
    {
        this(stmt, false);
    }
    
    public ODataResultSet(ODataPreparedStatement stmt, boolean trackCurrentOf) throws SQLException
    {
        execute(stmt);
        this.trackCurrentOf = trackCurrentOf;
        if(trackCurrentOf)
            ((DataStoreHelperOData)stmt.getCursor().getParent()).getCurrentOfManager().addQuery(stmt.getCursor().getCursorId(), this);
    }
    
    protected ClientEntity getCurrentEntity()
    {
        return ((ODataRecordIterator)iterator).currentEntity;
    }
    
    private void execute(IServicePreparedStatement iStmt) throws SQLException
    {
        this.stmt = (ODataPreparedStatement)iStmt;
        if(stmt.query.selectList != null)
        {
            for(IODataMap selectItem : stmt.query.selectList)
            {
                if(selectItem instanceof IODataMapName)
                {
                    skipBaseProperties = false;
                    break;
                }
            }
        }
        execute(stmt.query, stmt.parms);
    }
    
    
    ODataClient getClient() throws SQLException
    {
        return ((ODataConnection)stmt.getConnection()).client;
    }
    
    List constrainedProps;
    private void execute(ODataQuery query, Object[] parms) throws SQLException
    {
        EdmEntityType[] oEntityType = baseEntityType == null ? new EdmEntityType[1] : null;
        nextURI = query.build((ODataConnection)stmt.getConnection(), parms, oEntityType);
        constrainedProps = query.getConstrainedProps();
        if(baseEntityType == null && oEntityType != null)
            baseEntityType = oEntityType[0];
    }
    
    URI nextURI;
    
    @Override
    public  T getAs(Class reference, int columnIndex, T defaultValue) throws SQLException
    {
        try
        {
            value = (ClientValue)stmt.query.selectList[columnIndex-1].getValue(newServiceContext(), currentEntry);
            if(value == null)
                return defaultValue;
            T retValue = value.asPrimitive().toCastValue(reference);
            return retValue != null ? retValue : defaultValue;
        }catch(ClassCastException mayBeGeoSpatial)
        { // el generador manda un getVarchar para los geopoint
            try
            {
                if(value.asPrimitive().getTypeKind().isGeospatial())
                {
                    String wkt = value.toString();
                    wkt = wkt.substring(wkt.indexOf(';')+1).toUpperCase();
                    wkt = wkt.substring(0, wkt.lastIndexOf('\''));
                    return reference.cast(wkt);
                }else throw mayBeGeoSpatial;
            }catch(Exception e)
            {
                throw mayBeGeoSpatial;
            }            
        } catch (EdmPrimitiveTypeException ex)
        {
            try
            {
                if(ex.getCause() instanceof IllegalArgumentException &&
                   ex.getCause().getCause() instanceof ArithmeticException)
                { //@Hack: permito que si el modelo dice que retorna numerico sin decimales pero del server retornan con decimales se trunque
                    BigDecimal retVal = value.asPrimitive().toCastValue(BigDecimal.class).setScale(0, RoundingMode.FLOOR);
                    if(reference.isAssignableFrom(Long.class))
                        return reference.cast(retVal.longValueExact());
                    else if(reference.isAssignableFrom(Integer.class))
                        return reference.cast(retVal.intValueExact());
                    else if(reference.isAssignableFrom(Short.class))
                        return reference.cast(retVal.shortValueExact());
                    else if(reference.isAssignableFrom(Byte.class))
                        return reference.cast(retVal.byteValueExact());
                }                
            }catch(Exception ignored){}
            if(value.asPrimitive().getTypeKind() == EdmPrimitiveTypeKind.Guid)  // Caso particular de los Guid que en GX vienen como getString
                return reference.cast(value.toString()); 
            Logger.getLogger(ODataResultSet.class.getName()).log(Level.SEVERE, null, ex);
            return defaultValue;
        }
    }
    
    private ODataServiceContext newServiceContext() throws SQLException
    {
        return new ODataServiceContext(stmt.getConnection(), baseEntityType);
    }
    
    @Override
    public void close() throws SQLException
    {
        nextURI = null;
        if(trackCurrentOf)
            ((DataStoreHelperOData)stmt.getCursor().getParent()).getCurrentOfManager().removeQuery(stmt.getCursor().getCursorId());                
        super.close();
    }
    
    @Override
    public boolean wasNull()
	{
        return value == null || 
                (value.isPrimitive() && value.asPrimitive().toValue() == null);
    }    
    
    @Override
    public boolean isClosed() throws SQLException
    {
        return super.isClosed() && nextURI == null;
    }
            
    private ODataRecordIterator flattenRecords(ClientEntitySetIterator odataIterator)
    {
        ArrayList entities = new ArrayList<>();
        ArrayList>> iterators = new ArrayList<>();
        while(odataIterator.hasNext())
        {
            ClientEntity entity = odataIterator.next();
			List> records = new ArrayList<>(flattenRecord(flattenRecordSkip(entity, skipBaseProperties), true));
            entities.add(entity);
            iterators.add(records.iterator());
        }
        return new ODataRecordIterator(entities, iterators);
    }
    
    private HashMap flattenRecord(ClientEntity entity)
    {
        return flattenRecordSkip(entity, false);
    }

    private HashMap flattenRecordSkip(ClientEntity entity, boolean skipProperties)
    {
        for(String constrainedProp:constrainedProps)
        {
            ClientLink clientLink = entity.getNavigationLink(constrainedProp);
            // Si la query tiene una constraint sobre una entidad expandida y el resultado no trae la entidad, salteo el registro
            if(clientLink != null && 
               (clientLink.asInlineEntity() == null && 
                (clientLink.asInlineEntitySet() == null || clientLink.asInlineEntitySet().getEntitySet().getEntities().isEmpty())
               ))
            {
                return null;
            }
        }
        HashMap record = new HashMap<>();
        if(!skipProperties)
        {
            for(ClientProperty prop:entity.getProperties())
                record.put(prop.getName(), prop.getValue());
        }
        
        for(ClientLink link:entity.getNavigationLinks())            
        {
            ClientInlineEntity inlineEntity = link.asInlineEntity();            
            record.put(link.getName(), inlineEntity != null ? inlineEntity : link.asInlineEntitySet());
        }
        return record;
    }

    private Collection> flattenRecord(HashMap record)
    {
        return flattenRecord(record, false);
    }
    
    private Collection> flattenRecord(HashMap record, boolean isBase)
    {
        List> records = new ArrayList<>();
        String toFlattenKey = null;
        Object toFlattenValue = null;
        boolean anyValue = false;
        if(record == null)
            return records;
        for(String key:record.keySet())
        {
            Object curValue = record.get(key);
            anyValue |= curValue != null;
            if(curValue instanceof ClientCollectionValue ||
               curValue instanceof ClientInlineEntity ||
               curValue instanceof ClientComplexValue ||
               curValue instanceof ClientInlineEntitySet)
            {
                toFlattenKey = key;
                toFlattenValue = curValue;
                break;
            }
        }
        if(toFlattenValue == null)
        {
            if(anyValue)
                records.add(record);
        }else
        {
            record.remove(toFlattenKey);
            if(toFlattenValue instanceof ClientCollectionValue)
            {
                @SuppressWarnings("unchecked")
                ClientCollectionValue values = (ClientCollectionValue)toFlattenValue;
                if(values.isEmpty())
                {
                    if(isBase && selectsEntity(toFlattenKey))
                        return records;
                    else
                    {
                        record.put(toFlattenKey, null);
                        return flattenRecord(record, false);
                    }
                }else
                {
                    List> newRecords = new ArrayList<>();
                    for(ClientValue curValue:values)
                    {
                        HashMap newRecord = new HashMap<>(record);
                        newRecord.put(toFlattenKey, curValue);
                        newRecords.addAll(flattenRecord(newRecord));
                    }
                    for(HashMap newRecord:newRecords)
                        records.addAll(flattenRecord(newRecord));
                }
            }else if(toFlattenValue instanceof ClientInlineEntitySet)
            {
                ClientEntitySet entitySet = ((ClientInlineEntitySet)toFlattenValue).getEntitySet();
                if(entitySet.getEntities().isEmpty())
                {
                    if(isBase && selectsEntity(toFlattenKey))
                        return records;
                    else
                    {
                        record.put(toFlattenKey, null);
                        return flattenRecord(record);
                    }
                }else
                {
                    List> newRecords = new ArrayList<>();
                    for(ClientEntity entity:entitySet.getEntities())
                    {
                        HashMap inlineRecord = flattenRecord(entity);
                        for(HashMap flattenedValue:flattenRecord(inlineRecord))
                        {
                            HashMap newRecord = new HashMap<>(record);
                            newRecord.put(toFlattenKey, flattenedValue);
                            newRecords.addAll(flattenRecord(newRecord));
                        }
                    }
                    for(HashMap newRecord:newRecords)
                        records.addAll(flattenRecord(newRecord));
                }
            }else if(toFlattenValue instanceof ClientInlineEntity)
            {
                ClientInlineEntity inlineEntity = (ClientInlineEntity)toFlattenValue;
                HashMap inlineRecord = flattenRecord(inlineEntity.getEntity());
                List> newRecords = new ArrayList<>();
                for(HashMap flattenedValue:flattenRecord(inlineRecord))
                {
                    HashMap newRecord = new HashMap<>(record);
                    newRecord.put(toFlattenKey, flattenedValue);
                    newRecords.addAll(flattenRecord(newRecord));
                }
                for(HashMap newRecord:newRecords)
                    records.addAll(flattenRecord(newRecord));
            }else
            {
                ClientComplexValue complexValue = (ClientComplexValue)toFlattenValue;
                List> newRecords = new ArrayList<>();
                HashMap complexRecord = new HashMap<>();
                for(String key:complexValue.asJavaMap().keySet())
                    complexRecord.put(key, complexValue.get(key).getValue());
                for(HashMap flattenedValue:flattenRecord(complexRecord))
                {
                    HashMap newRecord = new HashMap<>(record);
                    newRecord.put(toFlattenKey, flattenedValue);
                    newRecords.addAll(flattenRecord(newRecord));
                }
                for(HashMap newRecord:newRecords)
                    records.addAll(flattenRecord(newRecord));
            }
        }
        return records;
    }
    
    private String getEntityName(String key)
    {
        try
        {
            return (String)newServiceContext().entity(key);
        }catch(SQLException e)
        {
            return key;
        }
    }
    
    private boolean selectsEntity(String key)
    {
        if(allSelectedKeys == null && stmt.query.selectList != null)
        {
            allSelectedKeys = new HashSet<>();
            for(int idx = 0; idx < stmt.query.selectList.length; idx++ )
            {
                IODataMap dataMap = stmt.query.selectList[idx];
                if(dataMap instanceof IODataMapName)
                    allSelectedKeys.add(dataMap.getName());
                else allSelectedKeys.add(getEntityName(dataMap.getName()));
            }
        }
        // Si se selecciona un campo que es collection pero en la entidad esta vacio, se saltea el registro
        // (lo hacemos para las propiedades de la entidad base
        return(allSelectedKeys != null && allSelectedKeys.contains(key));
    }
    
    ClientEntitySetIterator odataEntityIterator = null; 
    public ClientEntity nextEntity() throws SQLException
    {
        while(odataEntityIterator == null || !odataEntityIterator.hasNext())
        {
            if(odataEntityIterator != null)
                nextURI = odataEntityIterator.getNext();
            if(nextURI == null)
                return null;
            ODataRetrieveResponse> response = fetchNext();
            odataEntityIterator = response.getBody(); 
        }
        return odataEntityIterator.next();
    }

    ClientEntity firstEntity() throws SQLException
    {
        ODataRetrieveResponse> response = fetchNext();
        ClientEntitySetIterator odataIterator = response.getBody(); 
        if(odataIterator.hasNext())
            return odataIterator.next();
        else return null;
    }
    
    private ODataRetrieveResponse> fetchNext() throws SQLException
    {
        ODataRetrieveResponse> response;
        try
        {
            if(DebugFlag.DEBUG)
                    stmt.log.logComment(GXDBDebug.LOG_MIN, this, stmt.handle, "query=" + nextURI);
            response = getClient().getRetrieveRequestFactory().getEntitySetIteratorRequest(nextURI).execute();
        }catch(ODataServerErrorException ex)
        {
            String resStr = "";
            try
            {
                URLConnection conn = nextURI.toURL().openConnection();
                if(conn instanceof HttpURLConnection)
                {
                    conn.setRequestProperty("Accept", "*/*");
                    InputStream rawResponse;
                    try
                    {
                        rawResponse = conn.getInputStream();
                    }catch(IOException err){ rawResponse = ((HttpURLConnection) conn).getErrorStream(); }
                    resStr = new BufferedReader(new InputStreamReader(rawResponse)).lines().collect(java.util.stream.Collectors.joining("\n"));
                    rawResponse.close();
                }
            }catch(Exception ignored)
            { // esta excepción (al intentar leer el errorStream) se ignora porque se tira la original
            }
            throw new SQLException(String.format("%s%n%s", ex.getMessage(), resStr), ServiceError.INVALID_QUERY.getSqlState(), ServiceError.INVALID_QUERY.getCode(), ex);

        }catch(ODataRuntimeException ex)
        {
            throw new SQLException(ex.getMessage(), ServiceError.INVALID_QUERY.getSqlState(), ServiceError.INVALID_QUERY.getCode(), ex);
        }
        return response;
    }

    @Override
    public boolean next() throws SQLException
    {
        while((iterator == null || !iterator.hasNext()) && nextURI != null)
        {
            ODataRetrieveResponse> response = fetchNext();
            ClientEntitySetIterator odataIterator = response.getBody(); 
            iterator = flattenRecords(odataIterator);
            nextURI = odataIterator.getNext();
        }
        if(iterator != null && iterator.hasNext())
        {
            currentEntry = iterator.next();
            return true;
        }else
        {
            return false;
        }
    }
    
    class ODataRecordIterator implements Iterator>
    {
        private final Iterator entities;
        private final Iterator>> iterators;
        
        private ClientEntity currentEntity = null;
        private Iterator> currentIterator = null;
        
        public ODataRecordIterator(ArrayList entities, ArrayList>> iterators)
        {
            this.entities = entities.iterator();
            this.iterators = iterators.iterator();
        }

        @Override
        public boolean hasNext()
        {
            while(currentIterator == null || !currentIterator.hasNext())
            {
                if(!iterators.hasNext())
                    return false;
                currentIterator = iterators.next();
                currentEntity = entities.next();
            }
            return true;
        }

        @Override
        public HashMap next()
        {
            return currentIterator.next();
        }
    }
    
// JDK8
    @Override
    public  T getObject(int columnIndex, Class type)
	{
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    @Override
    public  T getObject(String columnLabel, Class type)
	{
        throw new UnsupportedOperationException("Not supported yet."); 
    } 
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy