Exemple #1
0
/**
 * Recursively filter input to output.
 *
 * @param filter the fitler to use.
 * @param in the input object.
 * @param out the filtered output object.
 */
static void _filter(
   DynamicObject& filter, DynamicObject& in, DynamicObject& out)
{
   if(!in.isNull())
   {
      DynamicObjectType inType = in->getType();

      if(inType == Map)
      {
         // check if this object matches filter
         if(_filterOne(filter, in))
         {
            out["@"]->append(in);
         }
      }
      if(inType == Map || inType == Array)
      {
         // filter each object
         DynamicObjectIterator i = in.getIterator();
         while(i->hasNext())
         {
            _filter(filter, i->next(), out);
         }
      }
   }
}
Exemple #2
0
/**
 * Expands a possible CURIE string into a full IRI. If the string is not
 * recognized as a CURIE that can be expanded into an IRI, then false is
 * returned.
 *
 * @param ctx the context to use.
 * @param str the string to expand (no <>).
 * @param iri the string to store the IRI in.
 * @param usedCtx a context to update if a value was used from "ctx".
 *
 * @return true if the string was expanded, false if not.
 */
static bool _expandCurie(
   DynamicObject& ctx, const char* str, string& iri,
   DynamicObject* usedCtx = NULL)
{
   bool rval = false;

   if(!ctx.isNull())
   {
      // try to find a colon
      const char* ptr = strchr(str, ':');
      if(ptr != NULL)
      {
         // get the potential CURIE prefix
         size_t len = ptr - str + 1;
         char prefix[len];
         snprintf(prefix, len, "%s", str);

         // see if the prefix is in the context
         if(ctx->hasMember(prefix))
         {
            // prefix found, normalize string
            DynamicObject& uri = ctx[prefix];
            len = strlen(uri->getString()) + strlen(ptr + 1) + 3;
            iri = StringTools::format("%s%s", uri->getString(), ptr + 1);
            if(usedCtx != NULL)
            {
               (*usedCtx)[prefix] = uri.clone();
            }
            rval = true;
         }
      }
   }

   return rval;
}
Exemple #3
0
bool JsonWriter::write(DynamicObject& dyno, OutputStream* os)
{
   bool rval = true;

   if(mStrict)
   {
      rval = !dyno.isNull();

      DynamicObjectType type;
      if(rval)
      {
         type = dyno->getType();
      }

      if(!rval || !(type == Map || type == Array))
      {
         ExceptionRef e = new Exception(
            "No JSON top-level Map or Array found.",
            "monarch.data.json.JsonWriter.InvalidJson");
         Exception::set(e);
         rval = false;
      }
   }

   if(rval)
   {
      ByteBuffer b(1024);
      BufferedOutputStream bos(&b, os);
      rval = write(dyno, &bos, mIndentLevel) && bos.flush();
   }

   return rval;
}
Exemple #4
0
bool JsonLd::addContext(
   DynamicObject& context, DynamicObject& in, DynamicObject& out)
{
   bool rval = true;

   // "a" is automatically shorthand for rdf type
   DynamicObject ctx = (context.isNull() ? DynamicObject() : context.clone());
   ctx["a"] = RDF_TYPE;

   // TODO: should context simplification be an option? (ie: remove context
   // entries that are not used in the output)

   // setup output context
   DynamicObject& contextOut = out["#"];
   contextOut->setType(Map);

   // apply context
   _applyContext(ctx, contextOut, NULL, in, out);

   // clean up
   if(contextOut->length() == 0)
   {
      out->removeMember("#");
   }

   return rval;
}
Exemple #5
0
/**
 * Recursively applies context to the given input object.
 *
 * @param ctx the context to use.
 * @param usedCtx the used context values.
 * @param predicate the related predicate or NULL if none.
 * @param in the input object.
 * @param out the contextualized output object.
 */
static void _applyContext(
   DynamicObject& ctx, DynamicObject& usedCtx,
   const char* predicate, DynamicObject& in, DynamicObject& out)
{
   if(in.isNull())
   {
      out.setNull();
   }
   else
   {
      // initialize output
      DynamicObjectType inType = in->getType();
      out->setType(inType);

      if(inType == Map)
      {
         // add context to each property in the map
         char* tmp = NULL;
         DynamicObjectIterator i = in.getIterator();
         while(i->hasNext())
         {
            // compact predicate
            DynamicObject& next = i->next();
            DynamicObject cp(NULL);
            const char* p;
            if(strcmp(i->getName(), "@") == 0)
            {
               p = "@";
            }
            else
            {
               cp = _compactString(ctx, usedCtx, NULL, i->getName(), &tmp);
               p = cp;
            }
            // recurse
            _applyContext(ctx, usedCtx, p, next, out[p]);
         }
         free(tmp);
      }
      else if(inType == Array)
      {
         // apply context to each object in the array
         DynamicObjectIterator i = in.getIterator();
         while(i->hasNext())
         {
            DynamicObject& next = i->next();
            _applyContext(ctx, usedCtx, predicate, next, out->append());
         }
      }
      // only strings need context applied, numbers & booleans don't
      else if(inType == String)
      {
         // compact string
         char* tmp = NULL;
         out = _compactString(ctx, usedCtx, predicate, in->getString(), &tmp);
         free(tmp);
      }
   }
}
Exemple #6
0
bool Map::isValid(
   DynamicObject& obj,
   ValidatorContext* context)
{
   bool rval = true;

   if(!obj.isNull() && obj->getType() == monarch::rt::Map)
   {
      Validators::iterator i;
      for(i = mValidators.begin(); i != mValidators.end(); ++i)
      {
         // only add a "." if this is not a root map
         if(context->getDepth() != 0)
         {
            context->pushPath(".");
         }
         context->pushPath(i->first);
         if(obj->hasMember(i->first))
         {
            // do not short-circuit to ensure all keys tested
            if(!i->second.validator->isValid(obj[i->first], context))
            {
               rval = false;
            }
         }
         else if(!i->second.validator->isOptional(context))
         {
            rval = false;
            DynamicObject detail =
               context->addError("monarch.validation.MissingField", &obj);
            detail["validator"] = "monarch.validator.Map";
            detail["message"] = "A required field has not been specified.";
            detail["key"] = i->first;
         }
         context->popPath();
         if(context->getDepth() > 0)
         {
            context->popPath();
         }
      }
   }
   else
   {
      rval = false;
      DynamicObject detail =
         context->addError("monarch.validation.TypeError", &obj);
      detail["validator"] = "monarch.validator.Map";
      detail["message"] = "The given object type must a mapping (Map) type";
   }

   return rval;
}
Exemple #7
0
/**
 * Recursively removes context from the given input object.
 *
 * @param ctx the context to use (changes during recursion as necessary).
 * @param in the input object.
 * @param out the normalized output object.
 * @param bnodeId the last blank node ID used.
 *
 * @return true on success, false on failure with exception set.
 */
static bool _removeContext(
   DynamicObject& ctx, DynamicObject& in, DynamicObject& out, int& bnodeId)
{
   bool rval = true;

   if(in.isNull())
   {
      out.setNull();
   }
   else
   {
      // initialize output
      DynamicObjectType inType = in->getType();
      out->setType(inType);

      // update context
      if(inType == Map && in->hasMember("#"))
      {
         ctx = in["#"];
      }

      if(inType == Map)
      {
         rval = _normalize(ctx, in, NULL, &out, bnodeId);
      }
      else if(inType == Array)
      {
         // strip context from each object in the array
         DynamicObjectIterator i = in.getIterator();
         while(i->hasNext())
         {
            DynamicObject& next = i->next();
            rval = _removeContext(ctx, next, out->append(), bnodeId);
         }
      }
   }

   return rval;
}
Exemple #8
0
bool Contains::isValid(
   DynamicObject& obj,
   ValidatorContext* context)
{
   bool rval = false;

   // check if objects are equal
   rval = (obj == mObject);

   // if not equal, check if target contains the validation object
   if(!rval && !obj.isNull() &&
      (obj->getType() == Array || obj->getType() == Map))
   {
      DynamicObjectIterator i = obj.getIterator();
      while(!rval && i->hasNext())
      {
         DynamicObject& next = i->next();
         rval = (next == mObject);
      }
   }

   if(!rval)
   {
      DynamicObject detail =
         context->addError("monarch.validation.NotFound", &obj);
      detail["validator"] = "monarch.validator.Contains";
      detail["expectedValue"] = mObject;
      detail["message"] = mErrorMessage ? mErrorMessage :
         "The input object was not equal to or found in the validator.";
   }

   if(rval)
   {
      context->addSuccess();
   }

   return rval;
}
Exemple #9
0
static DynamicObject _getExceptionGraph(
   DynamicObject& context, DynamicObject& autoContext, RdfaReader::Graph* g)
{
   DynamicObject rval(NULL);

   // clone auto context
   DynamicObject ctx = autoContext.clone();

   // use user-set context
   if(!context.isNull())
   {
      ctx = context.clone();
   }

   // save the old processor target and frame
   DynamicObject target = g->target;
   DynamicObject frame = g->frame;

   // use frame to embed error context in exception
   g->frame = DynamicObject();
   //g->frame["@context"] = JsonLd::createDefaultContext();
   g->frame["@type"] =
      "http://www.w3.org/ns/rdfa_processing_graph#Error";
   g->frame["http://www.w3.org/ns/rdfa_processing_graph#context"]->setType(Map);

   // finish processor graph
   g->target = DynamicObject();
   _finishGraph(ctx, g);
   rval = g->target;

   // reset old target and frame
   g->target = target;
   g->frame = frame;

   return rval;
}
Exemple #10
0
static bool _finishGraph(DynamicObject& ctx, RdfaReader::Graph* g)
{
   bool rval = true;

   // sort triples
   std::sort(g->triples.begin(), g->triples.end(), &_sortTriples);

   // create a mapping of subject to JSON-LD DynamicObject
   DynamicObject subjects(Map);
   for(RdfaReader::TripleList::iterator ti = g->triples.begin();
       ti != g->triples.end(); ++ti)
   {
      rdftriple* t = *ti;

      // get predicate
      const char* predicate = t->predicate;

      // JSON-LD encode object
      DynamicObject object(NULL);
      if(t->object_type == RDF_TYPE_IRI)
      {
         object = DynamicObject();

         // JSON-LD encode type
         if(strcmp(t->predicate, RDF_TYPE) == 0)
         {
            object = t->object;
            predicate = "@type";
         }
         else
         {
            object["@id"] = t->object;
         }
      }
      else if(t->object_type == RDF_TYPE_TYPED_LITERAL)
      {
         object = DynamicObject(Map);
         object["@value"] = t->object;
         object["@type"] = t->datatype;
         if(t->language != NULL && strlen(t->language) > 0)
         {
            object["@language"] = t->language;
         }
      }
      else
      {
         if(t->language != NULL && strlen(t->language) > 0)
         {
            object = DynamicObject(Map);
            object["@value"] = t->object;
            object["@language"] = t->language;
         }
         else
         {
            object = DynamicObject(String);
            object = t->object;
         }
      }

      // create/get the subject dyno
      DynamicObject& s = subjects[t->subject];
      if(!s->hasMember("@id"))
      {
         // JSON-LD encode subject
         s["@id"] = t->subject;
      }

      // add the predicate and object to the subject dyno
      _setPredicate(s, predicate, object);
   }

   // clear triples
   _freeTriples(g->triples);

   /* Note: At this point "subjects" holds a reference to every subject in
      the graph and each of those subjects has all of its predicates. Embedding
      specific objects in the target according to a frame is next, followed
      by adding the specific context. */
   if(rval)
   {
      DynamicObject out;
      if(!g->frame.isNull())
      {
         rval = JsonLd::frame(
            subjects.values(), g->frame, g->frameOptions, out);
         if(rval && out.isNull())
         {
            out = DynamicObject(g->frame->getType());
         }
      }
      else
      {
         rval = JsonLd::compact(
            subjects.values(), ctx, DynamicObject(Map), out);
      }

      if(rval)
      {
         // set target to output, preserving original dyno reference
         *(g->target) = *out;
      }
   }

   return rval;
}
Exemple #11
0
/**
 * Recursively normalizes the given input object.
 *
 * Input: A subject with predicates and possibly embedded other subjects.
 * Output: Either a map of normalized subjects OR a tree of normalized subjects.
 *
 * Normalization Algorithm:
 *
 * Replace the existing context if the input has '#'.
 *
 * For each key-value:
 * 1. Split the key on a colon and look for prefix in the context. If found,
 *    expand the key to an IRI, else it is already an IRI, add <>, save the
 *    new predicate to add to the output.
 * 2. If value is a Map, then it is a subject, set the predicate to the
 *    subject's '@' IRI value and recurse into it. Else goto #3.
 * 3. Look up the key in the context to find type coercion info. If not found,
 *    goto #4, else goto #5.
 * 4. Check the value for an integer, double, or boolean. If matched, set
 *    type according to xsd types. If not matched, look for <>, if found,
 *    do CURIE vs. IRI check like #1 and create appropriate value.
 * 5. If type coercion entry is a string, encode the value using the specific
 *    type. If it is an array, check the type in order of preference. If an
 *    unrecognized type (non-xsd, non-IRI) is provided, throw an exception.
 *
 * @param ctx the context to use (changes during recursion as necessary).
 * @param in the input object.
 * @param subjects a map of normalized subjects.
 * @param out a tree normalized objects.
 * @param bnodeId the last blank node ID used.
 *
 * @return true on success, false on failure with exception set.
 */
static bool _normalize(
   DynamicObject ctx, DynamicObject& in,
   DynamicObject* subjects, DynamicObject* out, int& bnodeId)
{
   bool rval = true;

   // FIXME: validate context (check for non-xsd types in type coercion arrays)

   if(!in.isNull())
   {
      // update context
      if(in->hasMember("#"))
      {
         ctx = in["#"];
      }

      // vars for normalization state
      DynamicObject subject(NULL);
      if(subjects != NULL)
      {
         subject = DynamicObject();
         subject->setType(Map);

         // assign blank node ID as needed
         if(!in->hasMember("@"))
         {
            string bnodeKey = _createBlankNodeId(++bnodeId);
            subject["@"] = bnodeKey.c_str();
         }
      }
      string nKey;

      // iterate over key-values
      DynamicObjectIterator i = in.getIterator();
      while(rval && i->hasNext())
      {
         DynamicObject& value = i->next();
         const char* key = i->getName();

         // skip context keys
         if(key[0] == '#')
         {
            continue;
         }

         // get normalized key
         nKey = _normalizeValue(ctx, key, RDF_TYPE_IRI, NULL, NULL);

         // put values in an array for single code path
         DynamicObject values;
         values->setType(Array);
         if(value->getType() == Array)
         {
            values.merge(value, true);

            // preserve array structure when not using subjects map
            if(out != NULL)
            {
               (*out)[nKey.c_str()]->setType(Array);
            }
         }
         else
         {
            values->append(value);
         }

         // normalize all values
         DynamicObjectIterator vi = values.getIterator();
         while(rval && vi->hasNext())
         {
            DynamicObject v = vi->next();
            if(v->getType() == Map)
            {
               if(subjects != NULL)
               {
                  // get a normalized subject for the value
                  string vSubject;
                  if(v->hasMember("@"))
                  {
                     // normalize existing subject
                     vSubject = _normalizeValue(
                        ctx, v["@"], RDF_TYPE_IRI, NULL, NULL);
                  }
                  else
                  {
                     // generate the next blank node ID in order to preserve
                     // the blank node embed in the code below
                     vSubject = _createBlankNodeId(bnodeId + 1);
                  }

                  // determine if value is a blank node
                  bool isBNode = _isBlankNode(v);

                  // update non-blank node subject (use value's subject IRI)
                  if(!isBNode)
                  {
                     _setPredicate(subject, nKey.c_str(), vSubject.c_str());
                  }

                  // recurse
                  rval = _normalize(ctx, v, subjects, out, bnodeId);

                  // preserve embedded blank node
                  if(rval && isBNode)
                  {
                     // remove embed from top-level subjects
                     DynamicObject embed = (*subjects)[vSubject.c_str()];
                     (*subjects)->removeMember(vSubject.c_str());
                     embed->removeMember("@");
                     _setEmbed(subject, nKey.c_str(), embed);
                  }
               }
               else
               {
                  // update out and recurse
                  DynamicObject next = (*out)[nKey.c_str()];
                  if(value->getType() == Array)
                  {
                     next = next->append();
                  }
                  else
                  {
                     next->setType(Map);
                  }
                  rval = _normalize(ctx, v, subjects, &next, bnodeId);
               }
            }
            else
            {
               _setPredicate(
                  (subjects != NULL) ? subject : *out, nKey.c_str(),
                  _normalizeValue(ctx, v, RDF_TYPE_UNKNOWN, key, NULL).c_str());
            }
         }
      }

      // add subject to map
      if(subjects != NULL)
      {
         (*subjects)[subject["@"]->getString()] = subject;
      }
   }

   return rval;
}
Exemple #12
0
/**
 * Normalizes a value using the given context.
 *
 * @param ctx the context.
 * @param value the value to normalize.
 * @param type the expected RDF type, use RDF_TYPE_UNKNOWN if not known.
 * @param predicate an optional predicate for the value (used to look up
 *           type coercion info).
 * @param usedCtx a context to update if a value was used from "ctx".
 *
 * @return the normalized string.
 */
static string _normalizeValue(
   DynamicObject& ctx, DynamicObject value,
   RdfType type, const char* predicate, DynamicObject* usedCtx)
{
   string rval;

   // "@" or "a"/RDF_TYPE predicates have values that are IRIs or CURIEs
   if(predicate != NULL &&
      (strcmp(predicate, "@") == 0 || strcmp(predicate, "a") == 0 ||
       strcmp(predicate, RDF_TYPE_NORM) == 0))
   {
      type = RDF_TYPE_IRI;
   }

   // IRI "@" is already normalized
   if(type == RDF_TYPE_IRI && strcmp(value, "@") == 0)
   {
      rval.assign(value);
   }
   // IRI "a" is special rdf type
   else if(type == RDF_TYPE_IRI && strcmp(value, "a") == 0)
   {
      rval.assign(RDF_TYPE_NORM);
   }
   else
   {
      string datatype;

      // look for type coercion info
      if(type == RDF_TYPE_UNKNOWN && predicate != NULL &&
         !ctx.isNull() && ctx->hasMember("#types") &&
         ctx["#types"]->hasMember(predicate))
      {
         DynamicObject& tci = ctx["#types"][predicate];
         if(usedCtx != NULL)
         {
            (*usedCtx)["#types"][predicate] = tci.clone();
         }

         // handle specific type
         if(tci->getType() == String)
         {
            rval = value->getString();
            datatype = _normalizeValue(ctx, tci, RDF_TYPE_IRI, NULL, usedCtx);
            type = (strcmp(datatype.c_str(), XSD_ANY_URI_NORM) == 0) ?
               RDF_TYPE_IRI : RDF_TYPE_TYPED_LITERAL;
         }
         // handle type preferences in order
         else
         {
            DynamicObjectIterator ti = tci.getIterator();
            while(type == RDF_TYPE_UNKNOWN && ti->hasNext())
            {
               // FIXME: if value works w/type, set value, datatype, type
               //type = RDF_TYPE_TYPED_LITERAL;
            }
         }
      }
      // determine type from DynamicObjectType
      else if(type == RDF_TYPE_UNKNOWN && value->getType() != String)
      {
         type = RDF_TYPE_TYPED_LITERAL;
         rval = value->getString();

         // handle native xsd types
         if(value->isNumber())
         {
            datatype = value->isInteger() ? XSD_INTEGER : XSD_DOUBLE;
         }
         else if(value->getType() == Boolean)
         {
            datatype = XSD_BOOLEAN;
         }
         else
         {
            // FIXME: this should never happen?
            datatype = XSD_ANY_TYPE;
         }
      }
      else
      {
         // JSON-LD decode
         RdfType t = type;
         _decode(value, type, rval, datatype);
         if(type == RDF_TYPE_TYPED_LITERAL)
         {
            _expandCurie(ctx, datatype.c_str(), datatype, usedCtx);
         }

         // preserve expected type
         if(t != RDF_TYPE_UNKNOWN)
         {
            type = t;
         }
      }

      // expand CURIE
      if(type == RDF_TYPE_IRI)
      {
         _expandCurie(ctx, rval.c_str(), rval, usedCtx);
      }

      // JSON-LD encode
      rval = _encode(type, rval.c_str(), datatype.c_str());
   }

   return rval;
}
Exemple #13
0
bool JsonWriter::write(DynamicObject& dyno, OutputStream* os, int level)
{
   bool rval = true;

   if(level < 0)
   {
      level = mIndentLevel;
   }

   if(dyno.isNull())
   {
      rval = os->write("null", 4);
   }
   else
   {
      switch(dyno->getType())
      {
         case String:
         {
            const char* temp = dyno->getString();
            size_t length = strlen(temp);

            // Note: UTF-8 has a maximum of 6-bytes per character when
            // encoded in json format ("\u1234")
            string encoded;
            encoded.reserve(length);
            encoded.push_back('"');
            char unicode[6];
            for(size_t i = 0; i < length; ++i)
            {
               unsigned char c = temp[i];
               if((c >= 0x5d /* && c <= 0x10FFFF */) ||
                  (c >= 0x23 && c <= 0x5B) ||
                  (c == 0x21) ||
                  (c == 0x20))
               {
                  // TODO: check this handles UTF-* properly
                  encoded.push_back(c);
               }
               else
               {
                  encoded.push_back('\\');
                  switch(c)
                  {
                     case '"': /* 0x22 */
                     case '\\': /* 0x5C */
                     // '/' is in the RFC but not required to be escaped
                     //case '/': /* 0x2F */
                        encoded.push_back(c);
                        break;
                     case '\b': /* 0x08 */
                        encoded.push_back('b');
                        break;
                     case '\f': /* 0x0C */
                        encoded.push_back('f');
                        break;
                     case '\n': /* 0x0A */
                        encoded.push_back('n');
                        break;
                     case '\r': /* 0x0D */
                        encoded.push_back('r');
                        break;
                     case '\t': /* 0x09 */
                        encoded.push_back('t');
                        break;
                     default:
                        // produces "u01af" (4 digits of 0-filled hex)
                        snprintf(unicode, 6, "u%04x", c);
                        encoded.append(unicode, 5);
                        break;
                  }
               }
            }

            // end string serialization and write encoded string
            encoded.push_back('"');
            rval = os->write(encoded.c_str(), encoded.length());
            break;
         }
         case Boolean:
         case Int32:
         case UInt32:
         case Int64:
         case UInt64:
         case Double:
         {
            const char* temp = dyno->getString();
            rval = os->write(temp, strlen(temp));
            break;
         }
         case Map:
         {
            // start map serialization
            rval = (mCompact || dyno->length() == 0) ?
               os->write("{", 1) : os->write("{\n", 2);

            // serialize each map member
            DynamicObjectIterator i = dyno.getIterator();
            while(rval && i->hasNext())
            {
               DynamicObject& next = i->next();

               // serialize indentation and start serializing member name
               if((rval =
                  writeIndentation(os, level + 1) &&
                  os->write("\"", 1) &&
                  os->write(i->getName(), strlen(i->getName()))))
               {
                  // end serializing member name, serialize member value
                  rval = ((mCompact) ?
                     os->write("\":", 2) : os->write("\": ", 3)) &&
                     write(next, os, level + 1);

                  // serialize delimiter if appropriate
                  if(rval && i->hasNext())
                  {
                     rval = os->write(",", 1);
                  }

                  // add formatting if appropriate
                  if(rval && !mCompact)
                  {
                     rval = os->write("\n", 1);
                  }
               }
            }

            // end map serialization
            if(dyno->length() > 0)
            {
               rval = rval && writeIndentation(os, level);
            }
            rval = rval && os->write("}", 1);
            break;
         }
         case Array:
         {
            // start array serialization
            rval = (mCompact || dyno->length() == 0) ?
               os->write("[", 1) : os->write("[\n", 2);

            // serialize each array element
            DynamicObjectIterator i = dyno.getIterator();
            while(rval && i->hasNext())
            {
               // serialize indentation and array value
               rval =
                  writeIndentation(os, level + 1) &&
                  write(i->next(), os, level + 1);

               // serialize delimiter if appropriate
               if(rval && i->hasNext())
               {
                  rval = os->write(",", 1);
               }

               // add formatting if appropriate
               if(rval && !mCompact)
               {
                  rval = os->write("\n", 1);
               }
            }

            // end array serialization
            if(dyno->length() > 0)
            {
               rval = rval && writeIndentation(os, level);
            }
            rval = rval && os->write("]", 1);
            break;
         }
      }
   }

   return rval;
}
Exemple #14
0
bool Int::isValid(
   DynamicObject& obj,
   ValidatorContext* context)
{
   bool rval;
   DynamicObjectType objType;

   rval = !obj.isNull();

   if(rval)
   {
      objType = obj->getType();
      if(objType == String)
      {
         objType = DynamicObject::determineType(obj->getString());
      }

      // type check
      rval =
         objType == Int32 ||
         objType == UInt32 ||
         objType == Int64 ||
         objType == UInt64;
   }

   if(!rval)
   {
      DynamicObject detail =
         context->addError("monarch.validation.ValueError", &obj);
      detail["validator"] = "monarch.validator.Int";
      detail["message"] =
         mErrorMessage ? mErrorMessage :
            "The given value type is required to be an integer.";
   }

   // absolute value of dyno value
   uint64_t val = 0;
   // flag if val is negative
   bool valneg = false;

   // get value for min/max check
   if(rval)
   {
      // get value and sign
      switch(objType)
      {
         case Int32:
         case Int64:
         {
            int64_t raw = obj->getInt64();
            valneg = (raw < 0);
            val = (uint64_t)(valneg ? -raw : raw);
            break;
         }
         case UInt32:
         case UInt64:
         {
            valneg = false;
            val = obj->getUInt64();
            break;
         }
         default:
            // never get here
            break;
      }
   }

   // min check
   if(rval)
   {
      if(mMinNegative != valneg)
      {
         // signs are different
         // val meets the minimum unless val is negative
         rval = !valneg;
      }
      else
      {
         // signs are the same
         // val meets the minimum if:
         // 1. val is positive and larger than mMin
         // 2. val is negative and smaller than mMin
         rval = (!valneg ? val >= mMin : val <= mMin);
      }

      // set exception on failure
      if(!rval)
      {
         DynamicObject detail =
            context->addError("monarch.validation.ValueError", &obj);
         detail["validator"] = "monarch.validator.Int";
         detail["message"] = mErrorMessage ? mErrorMessage :
            "The given integer value is less than the required minimum "
            "integer value.";
         detail["expectedMin"] = mMin;
      }
   }

   // max check
   if(rval)
   {
      if(mMaxNegative != valneg)
      {
         // signs are different
         // val meets the maximum unless val is positive
         rval = valneg;
      }
      else
      {
         // signs are the same
         // val meets the maximum if:
         // 1. val is positive and smaller than mMax
         // 2. val is negative and larger than mMax
         rval = (valneg ? val >= mMax : val <= mMax);
      }

      // set exception on failure
      if(!rval)
      {
         DynamicObject detail =
            context->addError("monarch.validation.ValueError", &obj);
         detail["validator"] = "monarch.validator.Int";
         detail["message"] = mErrorMessage ? mErrorMessage :
            "The given integer value is greater than the allowable maximum "
            "integer value.";
         detail["expectedMax"] = mMax;
      }
   }

   if(rval)
   {
      context->addSuccess();
   }

   return rval;
}
Exemple #15
0
bool DatabaseClient::mapInstance(
   const char* objType,
   DynamicObject& obj, DynamicObject& mapping,
   DynamicObject* userData)
{
   bool rval = false;

   /* Algorithm to create a column mapping to the values in the given object:

      1. Get the OR mapping for the object type.
      2. Iterate over the members in the mapping, assigning each one's
         associated information to a matching member in the instance object.
      3. Include any values from the instance object in the instance mapping
         validating and coercing types as needed.
   */

   /* The instance mapping format:
    *
    * mapping: {
    *    "tables": {} of table name to table entry
    *    "entries": [
    *       "table": the database table name
    *       "columns": [
    *          (cloned info from the OR mapping) +
    *          "member": the object member name
    *          "value": the value for the column (to apply via an operator,
    *             coerced to columnType)
    *          "userData": as given to this call
    *       ],
    *       "fkeys": [
    *          (cloned info from the OR mapping) +
    *          "member": the object member name
    *          "value": the value for "fcolumn" (coerced to columnType)
    *          "userData": as given to this call
    *       ]
    *    ]
    * }
    */

   // initialize mapping
   mapping["tables"]->setType(Map);
   mapping["entries"]->setType(Array);

   // get OR map for the given object type
   ObjRelMap orMap = getObjRelMap(objType);
   if(!orMap.isNull())
   {
      rval = true;
      DynamicObjectIterator i = orMap["members"].getIterator();
      while(rval && i->hasNext())
      {
         // get OR member info
         DynamicObject& info = i->next();

         // if object is NULL, then we want to get ALL members
         if(obj.isNull() || obj->hasMember(i->getName()))
         {
            // start building an instance mapping entry
            DynamicObject entry(NULL);

            // add/update entry based on table
            const char* table = info["table"]->getString();
            if(mapping["tables"]->hasMember(table))
            {
               // update existing entry
               entry = mapping["tables"][table];
            }
            else
            {
               // add a new entry
               entry = mapping["entries"]->append();
               entry["table"] = info["table"];
               entry["columns"]->setType(Array);
               entry["fkeys"]->setType(Array);
               if(orMap->hasMember("autoIncrement") &&
                  orMap["autoIncrement"]->hasMember(table))
               {
                  const char* id = orMap["autoIncrement"][table]->getString();
                  entry["autoIncrement"]["id"] = id;
                  entry["autoIncrement"]["type"]->setType(
                     orMap["members"][id]["columnType"]->getType());
               }
               mapping["tables"][table] = entry;
            }

            // clone info, add member name and user-data
            DynamicObject d = info.clone();
            d["member"] = i->getName();
            if(userData != NULL)
            {
               d["userData"] = *userData;
            }

            // set value
            if(obj.isNull())
            {
               d["value"];
            }
            else
            {
               // FIXME: validate data type
               d["value"] = obj[i->getName()].clone();
            }

            // coerce data type to match column type
            d["value"]->setType(d["columnType"]->getType());

            // add to entry based on group
            entry[info["group"]->getString()]->append(d);
         }
      }
   }

   return rval;
}