Package ch.agent.crnickl.mongodb

Source Code of ch.agent.crnickl.mongodb.ReadMethodsForChroniclesAndSeries

/*
*   Copyright 2012-2013 Hauser Olsson GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package ch.agent.crnickl.mongodb;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.bson.types.ObjectId;

import ch.agent.crnickl.T2DBException;
import ch.agent.crnickl.T2DBMsg;
import ch.agent.crnickl.T2DBMsg.E;
import ch.agent.crnickl.api.Attribute;
import ch.agent.crnickl.api.Chronicle;
import ch.agent.crnickl.api.DBObjectType;
import ch.agent.crnickl.api.Database;
import ch.agent.crnickl.api.Property;
import ch.agent.crnickl.api.Series;
import ch.agent.crnickl.api.Surrogate;
import ch.agent.crnickl.impl.ChronicleImpl;
import ch.agent.crnickl.impl.Permission;
import ch.agent.crnickl.impl.SeriesImpl;
import ch.agent.crnickl.mongodb.T2DBMMsg.J;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

/**
* A stateless object with methods providing read access to chronicles and
* series.
*
* @author Jean-Paul Vetterli
*/
public class ReadMethodsForChroniclesAndSeries extends MongoDatabaseMethods {
 
  public ReadMethodsForChroniclesAndSeries() {
  }

  /**
   * Find a chronicle corresponding to a surrogate. An
   * exception is thrown if there is no such chronicle.
   *
   * @param s a surrogate
   * @return a chronicle, never null
   * @throws T2DBException
   */
  public Chronicle getChronicle(Surrogate s) throws T2DBException {
    DBObject obj = getObject(s, false);
    Throwable cause = null;
    if (obj != null)
      try {
        Chronicle chronicle = unpack(s.getDatabase(), (BasicDBObject) obj);
        check(Permission.READ, chronicle);
        return chronicle;
      } catch (Exception e) {
        cause = e;
      }
    throw T2DBMsg.exception(cause, E.E40104, s.toString());
  }
 
  /**
   * Find a chronicle with a given parent and name.
   *
   * @param parent a chronicle
   * @param name a string
   * @return a chronicle or null
   * @throws T2DBException
   */
  public Chronicle getChronicleOrNull(Chronicle parent, String name) throws T2DBException {
    try {
      Database database = parent.getDatabase();
      DBObject obj = getMongoDB(database).getChronicles().findOne(
          mongoObject(MongoDatabase.FLD_CHRON_PARENT, getIdOrZero(parent),
          MongoDatabase.FLD_CHRON_NAME, name));
      if (obj != null) {
        Chronicle chronicle = unpack(database, (BasicDBObject) obj);
        check(Permission.READ, chronicle);
        return chronicle;
      } else
        return null;
    } catch (Exception e) {
      throw T2DBMsg.exception(e, E.E40123, name, parent.getName(true));
    }
  }
 
  public Collection<Chronicle> getChroniclesByParent(Chronicle parent) throws T2DBException {
    Collection<Chronicle> result = new ArrayList<Chronicle>();
    if (check(Permission.DISCOVER, parent, false)) {
      try {
        Database db = parent.getDatabase();
        DBCursor cursor = getMongoDB(db).getChronicles().find(
            mongoObject(MongoDatabase.FLD_CHRON_PARENT, getIdOrZero(parent)));
        try {
          while (cursor.hasNext()) {
            Chronicle chronicle = unpack(db, (BasicDBObject) cursor.next());
            check(Permission.READ, chronicle);
            result.add(chronicle);
          }
        } finally {
          cursor.close();
        }
      } catch (Exception e) {
        throw T2DBMsg.exception(e, E.E40122, parent.getName(true));
      }
    }
    return result;
  }
 
  /**
   * The method completes the attribute with the value found for one of the
   * entities in the list and returns true. If no value was found, the method
   * return false. If more than value was found, the one selected corresponds
   * to the first entity encountered in the list.
   *
   * @param chronicles a list of chronicles
   * @param attribute an attribute
   * @return true if any value found
   * @throws T2DBException
   */
  public boolean getAttributeValue(List<Chronicle> chronicles, Attribute<?> attribute) throws T2DBException {
    boolean found = false;
    Surrogate s = attribute.getProperty().getSurrogate();
    Database db = s.getDatabase();
    try {
      ObjectId[] chrOids = new ObjectId[chronicles.size()];
      int offset = 0;
      for(Chronicle chronicle : chronicles) {
        chrOids[offset++] = getId(chronicle);
      }
      DBCursor cursor = getMongoDB(db).getAttributes().find(
          mongoObject(MongoDatabase.FLD_ATTR_PROP,
              getId(s), MongoDatabase.FLD_ATTR_CHRON,
              mongoObject(Operator.IN.op(), chrOids)));
     
      offset = Integer.MAX_VALUE;
      BasicDBObject objAtOffset = null;
     
      while (cursor.hasNext()) {
        BasicDBObject obj = (BasicDBObject) cursor.next();
        ObjectId chrOid = obj.getObjectId(MongoDatabase.FLD_ATTR_CHRON);
        int offset1 = findOffset(chrOid, chronicles);
        if (offset1 < offset) {
          offset = offset1;
          objAtOffset = obj;
        }
        if (offset == 0)
          break;
      }

            if (objAtOffset != null) {
              ObjectId chrOid = objAtOffset.getObjectId(MongoDatabase.FLD_ATTR_CHRON);
              Surrogate chronicle = makeSurrogate(db, DBObjectType.CHRONICLE, chrOid);
              check(Permission.READ, chronicle);
              attribute.scan(objAtOffset.getString(MongoDatabase.FLD_ATTR_VALUE));
        String description = objAtOffset.getString(MongoDatabase.FLD_ATTR_DESC);
        if (description.length() > 0)
          attribute.setDescription(description);
              found = true;
            }
    } catch (Exception e) {
      throw T2DBMsg.exception(e, E.E40120, attribute.getProperty().getName());
    }
    return found;
  }
 
  /**
   * Return a list of chronicles with a given value for a given property.
   *
   * @param property a property
   * @param value a value
   * @param maxSize the maximum size of the result or 0 for no limit
   * @return a list of chronicles, possibly empty, never null
   * @throws T2DBException
   */
  public <T>List<Chronicle> getChroniclesByAttributeValue(Property<T> property, T value, int maxSize) throws T2DBException {
    List<Chronicle> result = new ArrayList<Chronicle>();
    Surrogate s = property.getSurrogate();
    Database db = s.getDatabase();
    try {
      DBCursor cursor = getMongoDB(db).getAttributes().find(
          mongoObject(MongoDatabase.FLD_ATTR_PROP,
              getId(s), MongoDatabase.FLD_ATTR_VALUE,
              property.getValueType().toString(value)));
      while (cursor.hasNext()) {
        BasicDBObject obj = (BasicDBObject) cursor.next();
        ObjectId chrOid = obj.getObjectId(MongoDatabase.FLD_ATTR_CHRON);
        Surrogate chronicle = makeSurrogate(db, DBObjectType.CHRONICLE, chrOid);
        try {
          check(Permission.READ, chronicle);
          result.add(new ChronicleImpl(chronicle));
        } catch (T2DBException e) {
          // ignore "unreadable" chronicles
        }
      }
    } catch (Exception e) {
      throw T2DBMsg.exception(e, E.E40119, property.getName(), value);
    }
    return result;
  }
 
  /**
   * Find a series corresponding to a surrogate.
   *
   * @param s a surrogate
   * @return a series or null
   * @throws T2DBException
   */
  public <T>Series<T> getSeries(Surrogate s) throws T2DBException {
    BasicDBObject obj = (BasicDBObject) getObject(s, false);
    Throwable cause = null;
    if (obj != null)
      try {
        ObjectId chrOid = obj.getObjectId(MongoDatabase.FLD_SER_CHRON);
        int serNum = obj.getInt(MongoDatabase.FLD_SER_NUM);
        Chronicle chronicle = new ChronicleImpl(
            makeSurrogate(s.getDatabase(), DBObjectType.CHRONICLE, chrOid));
        check(Permission.READ, chronicle);
        Series<T> series = new SeriesImpl<T>(chronicle, null, serNum, s);
        return series;
      } catch (Exception e) {
        cause = e;
      }
    throw T2DBMsg.exception(cause, E.E40104, s.toString());
  }
 
  /**
   * Return array of series in the positions corresponding to the
   * requested numbers. These numbers are series numbers within the schema,
   * not the series keys in the database. When a requested number in the array is
   * non-positive, the corresponding series will be null in the result. A
   * series can also be null when the requested number is positive but the
   * series has no data and has not been set up in the database (and therefore
   * has no series database key).
   * <p>
   * Note. There is no exception if the chronicle does not exist. This behavior makes
   * it easier to implement the wishful transaction protocol (i.e. testing again if it
   * was okay to delete a chronicle after it was deleted).
   *
   * @param chronicle a chronicle
   * @param names an array of simple names to plug into the series 
   * @param numbers an array of numbers
   * @return an array of series
   * @throws T2DBException
   */
  public <T>Series<T>[] getSeries(Chronicle chronicle, String[] names, int[] numbers) throws T2DBException {

    if (names.length != numbers.length)
      throw new IllegalArgumentException("lengths of names[] and numbers[] differ");

    @SuppressWarnings("unchecked")
    Series<T>[] result = new SeriesImpl[numbers.length];
    if (result.length > 0) {
      Map<Integer, Integer> map = new HashMap<Integer, Integer>();
      for (int i = 0; i < numbers.length; i++) {
        if (numbers[i] > 0) {
          if (map.put(numbers[i], i) != null)
            throw new IllegalArgumentException("duplicate number: " + numbers[i]);
        }
      }
      Surrogate s = chronicle.getSurrogate();
      Database db = s.getDatabase();
      try {
        check(Permission.READ, chronicle);
      } catch (Exception e) {
        throw T2DBMsg.exception(e, E.E40104, s.toString());
      }
      try {
        DBCursor cursor = getMongoDB(db).getSeries().find(
            mongoObject(MongoDatabase.FLD_SER_CHRON,
                getId(chronicle), MongoDatabase.FLD_SER_NUM,
                mongoObject(Operator.IN.op(), numbers)),
            mongoObject(MongoDatabase.FLD_SER_NUM, 1));
        while (cursor.hasNext()) {
          BasicDBObject obj = (BasicDBObject) cursor.next();
          ObjectId serOid = obj.getObjectId(MongoDatabase.FLD_ID);
          int serNumber = obj.getInt(MongoDatabase.FLD_SER_NUM);
          int i = map.get(serNumber);
          result[i] = new SeriesImpl<T>(chronicle,
              names[i], numbers[i],
              makeSurrogate(db, DBObjectType.SERIES, serOid));
        }
      } catch (Exception e) {
        throw T2DBMsg.exception(e, E.E40121, chronicle.getName(true));
      }
    }
    return result;
  }

  private Chronicle unpack(Database db, BasicDBObject obj) throws T2DBException {
    try {
      ObjectId id = obj.getObjectId(MongoDatabase.FLD_ID);
      Surrogate s = makeSurrogate(db, DBObjectType.CHRONICLE, id);
      ChronicleImpl.RawData data = new ChronicleImpl.RawData();
      data.setSurrogate(s);
      data.setName(obj.getString(MongoDatabase.FLD_CHRON_NAME));
      data.setDescription(obj.getString(MongoDatabase.FLD_CHRON_DESC));
      ObjectId parentId = obj.getObjectId(MongoDatabase.FLD_CHRON_PARENT);
      data.setCollection(parentId == null ? db.getTopChronicle() :
        new ChronicleImpl(makeSurrogate(db, DBObjectType.CHRONICLE, parentId)));
      ObjectId schemaId = obj.getObjectId(MongoDatabase.FLD_CHRON_SCHEMA);
      data.setSchema(schemaId == null ? null : makeSurrogate(db, DBObjectType.SCHEMA, schemaId));
      Chronicle chronicle = new ChronicleImpl(data);
      return chronicle;
    } catch (ClassCastException e) {
      throw T2DBMMsg.exception(e, J.J81010, obj.toString());
    }
  }
 
  private int findOffset(ObjectId id, List<Chronicle> chronicles) {
    int found = -1;
    int offset = 0;
    for(Chronicle chronicle : chronicles) {
      if (getId(chronicle).equals(id)) {
        found = offset;
        break;
      }
      offset++;
    }
    if (found < 0)
      throw new RuntimeException("chronicle not found: " + id);
    return found;
  }

}
TOP

Related Classes of ch.agent.crnickl.mongodb.ReadMethodsForChroniclesAndSeries

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.