text:
package org.oliot.epcis.service.query.mongodb;

import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBElement;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;

import org.apache.log4j.Level;
import org.bson.BsonArray;
import org.bson.BsonBoolean;
import org.bson.BsonDateTime;
import org.bson.BsonDocument;
import org.bson.BsonInt32;
import org.bson.BsonObjectId;
import org.bson.BsonString;
import org.bson.BsonType;
import org.bson.BsonValue;
import org.bson.types.ObjectId;
import org.json.JSONArray;
import org.json.JSONObject;
import org.oliot.epcis.configuration.Configuration;
import org.oliot.epcis.converter.mongodb.AggregationEventReadConverter;
import org.oliot.epcis.converter.mongodb.MasterDataReadConverter;
import org.oliot.epcis.converter.mongodb.MongoWriterUtil;
import org.oliot.epcis.converter.mongodb.ObjectEventReadConverter;
import org.oliot.epcis.converter.mongodb.QuantityEventReadConverter;
import org.oliot.epcis.converter.mongodb.TransactionEventReadConverter;
import org.oliot.epcis.converter.mongodb.TransformationEventReadConverter;
import org.oliot.epcis.security.OAuthUtil;
import org.oliot.epcis.service.subscription.MongoSubscription;
import org.oliot.epcis.service.subscription.MongoSubscriptionTask;
import org.oliot.epcis.service.subscription.TriggerEngine;
import org.oliot.model.epcis.AggregationEventType;
import org.oliot.model.epcis.AttributeType;
import org.oliot.model.epcis.EPCISEventListExtensionType;
import org.oliot.model.epcis.EPCISQueryBodyType;
import org.oliot.model.epcis.EPCISQueryDocumentType;
import org.oliot.model.epcis.EventListType;
import org.oliot.model.epcis.InvalidURIException;
import org.oliot.model.epcis.ObjectEventType;
import org.oliot.model.epcis.PollParameters;
import org.oliot.model.epcis.QuantityEventType;
import org.oliot.model.epcis.QueryParameterException;
import org.oliot.model.epcis.QueryParams;
import org.oliot.model.epcis.QueryResults;
import org.oliot.model.epcis.QueryResultsBody;
import org.oliot.model.epcis.QuerySchedule;
import org.oliot.model.epcis.QueryTooLargeException;
import org.oliot.model.epcis.SubscribeNotPermittedException;
import org.oliot.model.epcis.SubscriptionControls;
import org.oliot.model.epcis.SubscriptionControlsException;
import org.oliot.model.epcis.SubscriptionType;
import org.oliot.model.epcis.TransactionEventType;
import org.oliot.model.epcis.TransformationEventType;
import org.oliot.model.epcis.VocabularyElementType;
import org.oliot.model.epcis.VocabularyListType;
import org.oliot.model.epcis.VocabularyType;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.springframework.web.bind.annotation.PathVariable;

import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;

import static org.oliot.epcis.service.query.mongodb.MongoQueryUtil.*;
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.JobKey.jobKey;
import static org.quartz.TriggerBuilder.newTrigger;
import static org.quartz.TriggerKey.triggerKey;

public class MongoQueryService {

	public String subscribeEventQuery(SubscriptionType s, String userID, List<String> friendList)
			throws QueryParameterException, SubscriptionControlsException {

		// M27 - query params' constraint
		// M39 - query params' constraint
		String reason = checkConstraintSimpleEventQuery(s.getPollParameters());
		if (reason != null) {
			throw new QueryParameterException();
			// return makeErrorResult(reason, QueryParameterException.class);
		}

		// Existing subscription Check
		MongoCollection<BsonDocument> collection = Configuration.mongoDatabase.getCollection("Subscription",
				BsonDocument.class);
		BsonDocument exist = collection.find(new BsonDocument("subscriptionID", new BsonString(s.getSubscriptionID())))
				.first();
		if (exist != null) {
			return "SubscriptionID : " + s.getSubscriptionID() + " is already subscribed. ";
		}

		// cron Example
		// 0/10 * * * * ? : every 10 second

		if (s.getSchedule() != null && s.getTrigger() == null) {
			try {
				cronSchedule(s.getSchedule());
				addScheduleToQuartz(s);

			} catch (RuntimeException e) {
				throw new SubscriptionControlsException();
				// return makeErrorResult(e.toString(),
				// SubscriptionControlsException.class);
			}
		} else if (s.getSchedule() == null && s.getTrigger() != null) {
			// Add Trigger with Query
			TriggerEngine.addTriggerSubscription(s.getSubscriptionID(), s);
		} else {
			throw new SubscriptionControlsException();
			// return makeErrorResult("One of schedule, trigger should be null",
			// SubscriptionControlsException.class);
		}

		// Manage Subscription Persistently
		addScheduleToDB(s, userID, friendList);

		String retString = "SubscriptionID : " + s.getSubscriptionID() + " is successfully triggered. ";
		return retString;
	}

	public String subscribe(SubscriptionType s, String userID, List<String> friendList) throws QueryParameterException,
			SubscriptionControlsException, InvalidURIException, SubscribeNotPermittedException {
		// M20 : Throw an InvalidURIException for an incorrect dest argument in
		// the subscribe method in EPCIS Query Control Interface
		try {
			new URL(s.getDest());
		} catch (MalformedURLException e) {
			throw new InvalidURIException();
			// return makeErrorResult(e.toString(), InvalidURIException.class);
		}

		// M24 : Virtual Error Handling
		// Automatically processed by URI param
		// v1.2 not work
		if (s.getDest() == null) {
			throw new QueryParameterException();
			// return makeErrorResult("Fill the mandatory field in subscribe
			// method", QueryParameterException.class);
		}

		// M46
		if (s.getPollParameters().getQueryName().equals("SimpleMasterDataQuery")) {
			throw new SubscribeNotPermittedException();
			// return makeErrorResult("SimpleMasterDataQuery is not available in
			// subscription method", SubscribeNotPermittedException.class);
		}

		String retString = "";
		if (s.getPollParameters().getQueryName().equals("SimpleEventQuery")) {
			retString = subscribeEventQuery(s, userID, friendList);
		}

		return retString;
	}

	// Soap Query Adaptor
	public void subscribe(String queryName, QueryParams params, URI dest, SubscriptionControls controls,
			String subscriptionID) throws QueryParameterException, SubscriptionControlsException, InvalidURIException,
			SubscribeNotPermittedException {
		PollParameters p = new PollParameters(queryName, params);

		// Subscription Control Processing
		/*
		 * QuerySchedule: (Optional) Defines the periodic schedule on which the
		 * query is to be executed. See Section 8.2.5.3. Exactly one of schedule
		 * or trigger is required; if both are specified or both are omitted,
		 * the implementation SHALL raise a SubscriptionControls- Exception..
		 */
		QuerySchedule querySchedule = controls.getSchedule();
		String schedule = null;
		if (querySchedule != null) {
			String sec = querySchedule.getSecond();
			if (sec == null)
				sec = "*";
			String min = querySchedule.getMinute();
			if (min == null)
				min = "*";
			String hour = querySchedule.getHour();
			if (hour == null)
				hour = "*";
			String dayOfMonth = querySchedule.getDayOfMonth();
			if (dayOfMonth == null)
				dayOfMonth = "*";
			String month = querySchedule.getMonth();
			String dayOfWeek = querySchedule.getDayOfWeek();

			// either month or dayOfWeek should be ?
			// two are not null -> dayOfWeek = ?
			// one of two exists -> non-exist = ?
			// two are null -> month=* , dayOfWeek=?

			if (month == null && dayOfWeek == null) {
				month = "*";
				dayOfWeek = "?";
			} else if (month != null && dayOfWeek == null) {
				dayOfWeek = "?";
			} else if (month == null && dayOfWeek != null) {
				month = "?";
			} else {
				dayOfWeek = "?";
			}

			schedule = sec + " " + min + " " + hour + " " + dayOfMonth + " " + month + " " + dayOfWeek;
		}

		/*
		 * InitialRecordTime: (Optional) Specifies a time used to constrain what
		 * events are considered when processing the query when it is executed
		 * for the first time. See Section 8.2.5.2. If omitted, defaults to the
		 * time at which the subscription is created.
		 */
		XMLGregorianCalendar initialRecordTime = controls.getInitialRecordTime();
		String initialRecordTimeStr = null;
		if (initialRecordTime != null) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
			Date initialRecordDate = initialRecordTime.toGregorianCalendar().getTime();
			initialRecordTimeStr = sdf.format(initialRecordDate);
		}

		/*
		 * reportIfEmpty: If true, a QueryResults instance is always sent to the
		 * subscriber when the query is executed. If false, a QueryResults
		 * instance is sent to the subscriber only when the results are
		 * non-empty.
		 */
		Boolean reportIfEmpty = controls.isReportIfEmpty();

		SubscriptionType subscription = new SubscriptionType(subscriptionID, dest.toString(), schedule,
				controls.getTrigger(), initialRecordTimeStr, reportIfEmpty, p);

		subscribe(subscription, null, null);

	}

	public void unsubscribe(String subscriptionID) {

		MongoCollection<BsonDocument> collection = Configuration.mongoDatabase.getCollection("Subscription",
				BsonDocument.class);

		// Its size should be 0 or 1
		BsonDocument s = collection
				.findOneAndDelete(new BsonDocument("subscriptionID", new BsonString(subscriptionID)));

		if (s != null) {
			SubscriptionType subscription = new SubscriptionType(s);
			if (subscription.getSchedule() != null && subscription.getTrigger() == null) {
				// Remove from current Quartz
				removeScheduleFromQuartz(subscription);
			} else {
				TriggerEngine.removeTriggerSubscription(subscription.getSubscriptionID());
			}
		}
	}

	public String getSubscriptionIDsREST(@PathVariable String queryName) {

		JSONArray retArray = new JSONArray();

		MongoCollection<BsonDocument> collection = Configuration.mongoDatabase.getCollection("Subscription",
				BsonDocument.class);

		Iterator<BsonDocument> subIterator = collection
				.find(new BsonDocument("pollParameters.queryName", new BsonString(queryName)), BsonDocument.class)
				.iterator();

		while (subIterator.hasNext()) {
			BsonDocument subscription = subIterator.next();
			retArray.put(subscription.getString("subscriptionID").getValue());
		}

		return retArray.toString(1);
	}

	public List<String> getSubscriptionIDs(String queryName) {

		List<String> retList = new ArrayList<String>();

		MongoCollection<BsonDocument> collection = Configuration.mongoDatabase.getCollection("Subscription",
				BsonDocument.class);

		Iterator<BsonDocument> subIterator = collection
				.find(new BsonDocument("pollParameters.queryName", new BsonString(queryName)), BsonDocument.class)
				.iterator();

		while (subIterator.hasNext()) {
			BsonDocument subscription = subIterator.next();
			retList.add(subscription.getString("subscriptionID").asString().getValue());
		}

		return retList;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public String pollEventQuery(PollParameters p, String userID, List<String> friendList, String subscriptionID)
			throws QueryParameterException, QueryTooLargeException {

		// M27 - query params' constraint
		// M39 - query params' constraint
		String reason = checkConstraintSimpleEventQuery(p);

		if (reason != null) {
			throw new QueryParameterException();
			// return makeErrorResult(reason, QueryParameterException.class);
		}

		// Make Base Result Document
		EPCISQueryDocumentType epcisQueryDocumentType = null;

		if (p.getFormat() == null || p.getFormat().equals("XML")) {
			epcisQueryDocumentType = makeBaseResultDocument(p.getQueryName(), subscriptionID);
		} else if (p.getFormat().equals("JSON")) {
			// Do Nothing
		} else {
			throw new QueryParameterException();
			// return makeErrorResult("format param should be one of XML or
			// JSON", QueryParameterException.class);
		}

		// Prepare container which query results are included
		// eventObjects : Container which all the query results (events) will be
		// contained
		List<Object> eventObjects = null;
		if (p.getFormat() == null || p.getFormat().equals("XML")) {
			eventObjects = epcisQueryDocumentType.getEPCISBody().getQueryResults().getResultsBody().getEventList()
					.getObjectEventOrAggregationEventOrQuantityEvent();
		} else {
			// foramt == JSON -> Do Nothing
		}

		// Event Collection
		MongoCollection<BsonDocument> collection = Configuration.mongoDatabase.getCollection("EventData",
				BsonDocument.class);
		// Queries
		BsonArray queryList = makeQueryObjects(p, userID, friendList);

		// Merge All the queries with $and
		BsonDocument baseQuery = new BsonDocument();
		FindIterable<BsonDocument> cursor;
		if (queryList.isEmpty() == false) {
			BsonArray aggreQueryList = new BsonArray();
			for (int i = 0; i < queryList.size(); i++) {
				aggreQueryList.add(queryList.get(i));
			}
			baseQuery.put("$and", aggreQueryList);
			// Query
			cursor = collection.find(baseQuery);
		} else {
			cursor = collection.find();
		}
		// Sort and Limit
		cursor = makeSortedLimitedCursor(cursor, p.getOrderBy(), p.getOrderDirection(), p.getEventCountLimit());

		JSONArray retArray = new JSONArray();

		MongoCursor<BsonDocument> slCursor = cursor.iterator();
		while (slCursor.hasNext()) {
			BsonDocument dbObject = slCursor.next();

			String eventTypeInDoc = dbObject.getString("eventType").getValue();

			if (OAuthUtil.isAccessible(userID, friendList, dbObject) == false) {
				continue;
			}

			if (!isPostFilterPassed(eventTypeInDoc, dbObject, p.getParams()))
				continue;

			if (p.getFormat() == null || p.getFormat().equals("XML")) {
				if (eventTypeInDoc.equals("AggregationEvent")) {
					AggregationEventReadConverter con = new AggregationEventReadConverter();
					JAXBElement element = new JAXBElement(new QName("AggregationEvent"), AggregationEventType.class,
							con.convert(dbObject));
					eventObjects.add(element);
				} else if (eventTypeInDoc.equals("ObjectEvent")) {
					ObjectEventReadConverter con = new ObjectEventReadConverter();
					JAXBElement element = new JAXBElement(new QName("ObjectEvent"), ObjectEventType.class,
							con.convert(dbObject));
					eventObjects.add(element);
				} else if (eventTypeInDoc.equals("QuantityEvent")) {
					QuantityEventReadConverter con = new QuantityEventReadConverter();
					JAXBElement element = new JAXBElement(new QName("QuantityEvent"), QuantityEventType.class,
							con.convert(dbObject));
					eventObjects.add(element);
				} else if (eventTypeInDoc.equals("TransactionEvent")) {
					TransactionEventReadConverter con = new TransactionEventReadConverter();
					JAXBElement element = new JAXBElement(new QName("TransactionEvent"), TransactionEventType.class,
							con.convert(dbObject));
					eventObjects.add(element);
				} else if (eventTypeInDoc.equals("TransformationEvent")) {
					TransformationEventReadConverter con = new TransformationEventReadConverter();
					TransformationEventType transformationEvent = con.convert(dbObject);
					EPCISEventListExtensionType extension = new EPCISEventListExtensionType();
					extension.setTransformationEvent(transformationEvent);
					JAXBElement element = new JAXBElement(new QName("extension"), EPCISEventListExtensionType.class,
							extension);
					eventObjects.add(element);
				}
			} else {
				dbObject.remove("_id");
				retArray.put(new JSONObject(dbObject.toJson()));
			}
		}

		// M44
		if (p.getMaxEventCount() != null) {
			if (p.getFormat() == null || p.getFormat().equals("XML")) {
				if (eventObjects.size() > p.getMaxEventCount()) {
					throw new QueryTooLargeException();
					// return makeErrorResult("Violate maxEventCount",
					// QueryTooLargeException.class);
				}
			} else {
				if (retArray.length() > p.getMaxEventCount()) {
					throw new QueryTooLargeException();
					// return makeErrorResult("Violate maxEventCount",
					// QueryTooLargeException.class);
				}
			}
		}

		if (p.getFormat() == null || p.getFormat().equals("XML")) {
			StringWriter sw = new StringWriter();
			JAXB.marshal(epcisQueryDocumentType, sw);
			return sw.toString();
		} else {
			return retArray.toString(1);
		}
	}

	public String pollMasterDataQuery(PollParameters p, String userID, List<String> friendList)
			throws QueryTooLargeException, QueryParameterException {

		// Required Field Check
		if (p.getIncludeAttributes() == null || p.getIncludeChildren() == null) {
			throw new QueryTooLargeException();
			// return makeErrorResult("SimpleMasterDataQuery's Required Field:
			// includeAttributes, includeChildren",
			// QueryTooLargeException.class);
		}

		// Make Base Result Document
		EPCISQueryDocumentType epcisQueryDocumentType = null;
		JSONArray retArray = new JSONArray();

		if (p.getFormat() == null || p.getFormat().equals("XML")) {
			epcisQueryDocumentType = makeBaseResultDocument(p.getQueryName(), null);
		} else if (p.getFormat().equals("JSON")) {
			// Do Nothing
		} else {
			throw new QueryParameterException();
			// return makeErrorResult("format param should be one of XML or
			// JSON", QueryParameterException.class);
		}

		MongoCollection<BsonDocument> collection = Configuration.mongoDatabase.getCollection("MasterData",
				BsonDocument.class);

		// Make Query
		BsonArray queryList = makeMasterQueryObjects(p);

		// Merge All the queries with $and
		BsonDocument baseQuery = new BsonDocument();
		FindIterable<BsonDocument> cursor;
		if (queryList.isEmpty() == false) {
			BsonArray aggreQueryList = new BsonArray();
			for (int i = 0; i < queryList.size(); i++) {
				aggreQueryList.add(queryList.get(i));
			}
			baseQuery.put("$and", aggreQueryList);
			// Query
			cursor = collection.find(baseQuery);
		} else {
			cursor = collection.find();
		}

		// Cursor needed to ordered
		List<VocabularyType> vList = new ArrayList<>();

		MongoCursor<BsonDocument> slCursor = cursor.iterator();
		while (slCursor.hasNext()) {
			BsonDocument dbObject = slCursor.next();

			if (p.getFormat() == null || p.getFormat().equals("XML")) {
				MasterDataReadConverter con = new MasterDataReadConverter();
				VocabularyType vt = con.convert(dbObject);

				if (vt.getVocabularyElementList() != null) {
					if (vt.getVocabularyElementList().getVocabularyElement() != null) {
						List<VocabularyElementType> vetList = vt.getVocabularyElementList().getVocabularyElement();
						for (int i = 0; i < vetList.size(); i++) {
							VocabularyElementType vet = vetList.get(i);
							if (p.getIncludeAttributes() == false) {
								vet.setAttribute(null);
							} else if (p.getIncludeAttributes() == true && p.getAttributeNames() != null) {
								String[] attrArr = p.getAttributeNames().split(",");
								Set<String> attrSet = new HashSet<String>();
								for (int j = 0; j < attrArr.length; j++) {
									attrSet.add(attrArr[j].trim());
								}

								List<AttributeType> atList = vet.getAttribute();
								List<AttributeType> filteredList = new ArrayList<AttributeType>();
								for (int j = 0; j < atList.size(); j++) {
									if (attrSet.contains(atList.get(j).getId())) {
										filteredList.add(atList.get(j));
									}
								}
								vet.setAttribute(filteredList);
							}

							if (p.getIncludeChildren() == false) {
								vet.setChildren(null);
							}
						}
					}
				}
				vList.add(vt);
			} else {
				dbObject.remove("_id");
				if (p.getIncludeAttributes() == false) {
					dbObject.remove("attributes");
				} else if (p.getIncludeAttributes() == true && p.getAttributeNames() != null) {
					String[] attrArr = p.getAttributeNames().split(",");
					Set<String> attrSet = new HashSet<String>();
					for (int j = 0; j < attrArr.length; j++) {
						attrSet.add(attrArr[j].trim());
					}
					BsonDocument attrObject = dbObject.get("attributes").asDocument();
					BsonDocument newObject = new BsonDocument();
					if (attrObject != null) {
						Iterator<String> attrKeys = attrObject.keySet().iterator();
						while (attrKeys.hasNext()) {
							String attrKey = attrKeys.next();
							if (attrSet.contains(attrKey)) {
								newObject.put(attrKey, attrObject.get(attrKey));
							}
						}
					}
					dbObject.put("attributes", newObject);

				}
				if (p.getIncludeChildren() == false) {
					dbObject.remove("children");
				}
				retArray.put(dbObject);
			}

		}

		if (p.getFormat() == null || p.getFormat().equals("XML")) {
			QueryResultsBody qbt = epcisQueryDocumentType.getEPCISBody().getQueryResults().getResultsBody();

			VocabularyListType vlt = new VocabularyListType();
			vlt.setVocabulary(vList);
			qbt.setVocabularyList(vlt);
		}

		// M47
		if (p.getMaxElementCount() != null) {
			try {
				if (p.getFormat() == null || p.getFormat().equals("XML")) {
					if (vList.size() > p.getMaxElementCount()) {
						throw new QueryTooLargeException();
						// return makeErrorResult("Too Large Master Data
						// result", QueryTooLargeException.class);
					}
				} else {
					if (retArray.length() > p.getMaxElementCount()) {
						throw new QueryTooLargeException();
						// return makeErrorResult("Too Large Master Data
						// result", QueryTooLargeException.class);
					}
				}
			} catch (NumberFormatException e) {

			}
		}
		if (p.getFormat() == null || p.getFormat().equals("XML")) {
			StringWriter sw = new StringWriter();
			JAXB.marshal(epcisQueryDocumentType, sw);
			return sw.toString();
		} else {
			return retArray.toString(1);
		}
	}

	// Soap Service Adaptor
	public String poll(String queryName, QueryParams queryParams)
			throws QueryParameterException, QueryTooLargeException {
		PollParameters p = new PollParameters(queryName, queryParams);
		return poll(p, null, null, null);
	}

	public String poll(PollParameters p, String userID, List<String> friendList, String subscriptionID)
			throws QueryParameterException, QueryTooLargeException {

		// M24
		if (p.getQueryName() == null) {
			// It is not possible, automatically filtered by URI param
			throw new QueryParameterException();
			// return makeErrorResult("queryName is mandatory field in poll
			// method", QueryParameterException.class);
		}

		if (p.getQueryName().equals("SimpleEventQuery"))
			return pollEventQuery(p, userID, friendList, subscriptionID);

		if (p.getQueryName().equals("SimpleMasterDataQuery"))
			return pollMasterDataQuery(p, userID, friendList);
		return "";
	}

	static BsonDateTime getTimeMillis(String standardDateString) {
		try {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
			GregorianCalendar eventTimeCalendar = new GregorianCalendar();
			eventTimeCalendar.setTime(sdf.parse(standardDateString));
			return new BsonDateTime(eventTimeCalendar.getTimeInMillis());
		} catch (ParseException e) {
			try {
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
				GregorianCalendar eventTimeCalendar = new GregorianCalendar();
				eventTimeCalendar.setTime(sdf.parse(standardDateString));
				return new BsonDateTime(eventTimeCalendar.getTimeInMillis());
			} catch (ParseException e1) {
				Configuration.logger.log(Level.ERROR, e1.toString());
			}
		}
		// Never Happened
		return null;
	}

	boolean isExtraParameter(String paramName) {

		if (paramName.contains("eventTime"))
			return false;
		if (paramName.contains("recordTime"))
			return false;
		if (paramName.contains("errorDeclarationTime"))
			return false;
		if (paramName.contains("action"))
			return false;
		if (paramName.contains("bizStep"))
			return false;
		if (paramName.contains("disposition"))
			return false;
		if (paramName.contains("readPoint"))
			return false;
		if (paramName.contains("bizLocation"))
			return false;
		if (paramName.contains("bizTransaction"))
			return false;
		if (paramName.contains("source"))
			return false;
		if (paramName.contains("destination"))
			return false;
		if (paramName.contains("transformationID"))
			return false;
		if (paramName.contains("ILMD"))
			return false;
		if (paramName.contains("eventID"))
			return false;
		if (paramName.contains("errorReason"))
			return false;
		if (paramName.contains("correctiveEventID"))
			return false;
		if (paramName.contains("errorDeclaration"))
			return false;
		if (paramName.contains("ERROR_DECLARATION"))
			return false;
		if (paramName.contains("INNER"))
			return false;

		return true;
	}

	private String checkConstraintSimpleEventQuery(PollParameters p) throws QueryParameterException {

		// M27
		try {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
			if (p.getGE_eventTime() != null)
				sdf.parse(p.getGE_eventTime());
			if (p.getLT_eventTime() != null)
				sdf.parse(p.getLT_eventTime());
			if (p.getGE_recordTime() != null)
				sdf.parse(p.getGE_recordTime());
			if (p.getLT_recordTime() != null)
				sdf.parse(p.getLT_recordTime());
		} catch (ParseException e) {
			try {
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
				if (p.getGE_eventTime() != null)
					sdf.parse(p.getGE_eventTime());
				if (p.getLT_eventTime() != null)
					sdf.parse(p.getLT_eventTime());
				if (p.getGE_recordTime() != null)
					sdf.parse(p.getGE_recordTime());
				if (p.getLT_recordTime() != null)
					sdf.parse(p.getLT_recordTime());
			} catch (ParseException e1) {
				throw new QueryParameterException();
				// return makeErrorResult(e.toString(),
				// QueryParameterException.class);
			}
		}

		// M27
		if (p.getOrderBy() != null) {
			/*
			 * if (!orderBy.equals("eventTime") &&
			 * !orderBy.equals("recordTime")) { return makeErrorResult(
			 * "orderBy should be eventTime or recordTime",
			 * QueryParameterException.class); }
			 */
			if (p.getOrderDirection() != null) {
				if (!p.getOrderDirection().equals("ASC") && !p.getOrderDirection().equals("DESC")) {
					throw new QueryParameterException();
					// return makeErrorResult("orderDirection should be ASC or
					// DESC", QueryParameterException.class);
				}
			}
		}

		// M27
		if (p.getEventCountLimit() != null) {
			if (p.getEventCountLimit() <= 0) {
				throw new QueryParameterException();
				// return makeErrorResult("eventCount should be natural number",
				// QueryParameterException.class);
			}
		}

		// M27
		if (p.getMaxEventCount() != null) {
			if (p.getMaxEventCount() <= 0) {
				throw new QueryParameterException();
				// return makeErrorResult("maxEventCount should be natural
				// number", QueryParameterException.class);
			}
		}

		// M39
		if (p.getEQ_action() != null) {

			String[] actionArr = p.getEQ_action().split(",");
			for (String action : actionArr) {
				action = action.trim();
				if (action.equals(""))
					continue;
				if (!action.equals("ADD") && !action.equals("OBSERVE") && !action.equals("DELETE")) {
					throw new QueryParameterException();
					// return makeErrorResult("EQ_action: ADD | OBSERVE |
					// DELETE", QueryParameterException.class);
				}
			}
		}

		// M42
		if (p.getEventCountLimit() != null && p.getMaxEventCount() != null) {
			throw new QueryParameterException();
			// return makeErrorResult("One of eventCountLimit and maxEventCount
			// should be omitted", QueryParameterException.class);
		}
		return null;
	}

	public EPCISQueryDocumentType makeBaseResultDocument(String queryName, String subscriptionID) {
		// Make Base Result Document
		EPCISQueryDocumentType epcisQueryDocumentType = new EPCISQueryDocumentType();
		EPCISQueryBodyType epcisBody = new EPCISQueryBodyType();
		epcisQueryDocumentType.setEPCISBody(epcisBody);
		QueryResults queryResults = new QueryResults();
		queryResults.setQueryName(queryName);
		epcisBody.setQueryResults(queryResults);
		QueryResultsBody queryResultsBody = new QueryResultsBody();
		queryResults.setResultsBody(queryResultsBody);
		if (subscriptionID != null)
			queryResults.setSubscriptionID(subscriptionID);
		EventListType eventListType = new EventListType();
		queryResultsBody.setEventList(eventListType);
		// Object instanceof JAXBElement
		List<Object> eventObjects = new ArrayList<Object>();
		eventListType.setObjectEventOrAggregationEventOrQuantityEvent(eventObjects);
		return epcisQueryDocumentType;
	}

	@SuppressWarnings({ "rawtypes", "unused" })
	private String makeErrorResult(String err, Class type) {
		if (type == InvalidURIException.class) {
			InvalidURIException e = new InvalidURIException();
			e.setReason(err);
			EPCISQueryDocumentType retDoc = new EPCISQueryDocumentType();
			EPCISQueryBodyType retBody = new EPCISQueryBodyType();
			retBody.setInvalidURIException(e);
			retDoc.setEPCISBody(retBody);
			StringWriter sw = new StringWriter();
			JAXB.marshal(retDoc, sw);
			return sw.toString();
		}
		if (type == QueryParameterException.class) {
			QueryParameterException e = new QueryParameterException();
			e.setReason(err);
			EPCISQueryDocumentType retDoc = new EPCISQueryDocumentType();
			EPCISQueryBodyType retBody = new EPCISQueryBodyType();
			retBody.setQueryParameterException(e);
			retDoc.setEPCISBody(retBody);
			StringWriter sw = new StringWriter();
			JAXB.marshal(retDoc, sw);
			return sw.toString();
		}
		if (type == SubscriptionControlsException.class) {
			SubscriptionControlsException e = new SubscriptionControlsException();
			e.setReason(err);
			EPCISQueryDocumentType retDoc = new EPCISQueryDocumentType();
			EPCISQueryBodyType retBody = new EPCISQueryBodyType();
			retBody.setSubscriptionControlsException(e);
			retDoc.setEPCISBody(retBody);
			StringWriter sw = new StringWriter();
			JAXB.marshal(retDoc, sw);
			return sw.toString();
		}
		if (type == QueryTooLargeException.class) {
			QueryTooLargeException e = new QueryTooLargeException();
			e.setReason(err);
			EPCISQueryDocumentType retDoc = new EPCISQueryDocumentType();
			EPCISQueryBodyType retBody = new EPCISQueryBodyType();
			retBody.setQueryTooLargeException(e);
			retDoc.setEPCISBody(retBody);
			StringWriter sw = new StringWriter();
			JAXB.marshal(retDoc, sw);
			return sw.toString();
		}
		if (type == SubscribeNotPermittedException.class) {
			SubscribeNotPermittedException e = new SubscribeNotPermittedException();
			e.setReason(err);
			EPCISQueryDocumentType retDoc = new EPCISQueryDocumentType();
			EPCISQueryBodyType retBody = new EPCISQueryBodyType();
			retBody.setSubscribeNotPermittedException(e);
			retDoc.setEPCISBody(retBody);
			StringWriter sw = new StringWriter();
			JAXB.marshal(retDoc, sw);
			return sw.toString();
		}
		return null;
	}

	private FindIterable<BsonDocument> makeSortedLimitedCursor(FindIterable<BsonDocument> cursor, String orderBy,
			String orderDirection, Integer eventCountLimit) {
		/**
		 * orderBy : If specified, names a single field that will be used to
		 * order the results. The orderDirection field specifies whether the
		 * ordering is in ascending sequence or descending sequence. Events
		 * included in the result that lack the specified field altogether may
		 * occur in any position within the result event list. The value of this
		 * parameter SHALL be one of: eventTime, recordTime, or the fully
		 * qualified name of an extension field whose type is Int, Float, Time,
		 * or String. A fully qualified fieldname is constructed as for the
		 * EQ_fieldname parameter. In the case of a field of type String, the
		 * ordering SHOULD be in lexicographic order based on the Unicode
		 * encoding of the strings, or in some other collating sequence
		 * appropriate to the locale. If omitted, no order is specified. The
		 * implementation MAY order the results in any order it chooses, and
		 * that order MAY differ even when the same query is executed twice on
		 * the same data. (In EPCIS 1.0, the value quantity was also permitted,
		 * but its use is deprecated in EPCIS 1.1.)
		 *
		 * orderDirection : If specified and orderBy is also specified,
		 * specifies whether the results are ordered in ascending or descending
		 * sequence according to the key specified by orderBy. The value of this
		 * parameter must be one of ASC (for ascending order) or DESC (for
		 * descending order); if not, the implementation SHALL raise a
		 * QueryParameterException. If omitted, defaults to DESC.
		 */

		// Update Query with ORDER and LIMIT
		if (orderBy != null) {
			orderBy = MongoWriterUtil.encodeMongoObjectKey(orderBy);
			// Currently only eventTime, recordTime can be used
			if (orderBy.trim().equals("eventTime")) {
				if (orderDirection != null) {
					if (orderDirection.trim().equals("ASC")) {
						cursor = cursor.sort(new BsonDocument("eventTime", new BsonInt32(1)));
					} else if (orderDirection.trim().equals("DESC")) {
						cursor = cursor.sort(new BsonDocument("eventTime", new BsonInt32(-1)));
					}
				} else {
					cursor = cursor.sort(new BsonDocument("eventTime", new BsonInt32(-1)));
				}
			} else if (orderBy.trim().equals("recordTime")) {
				if (orderDirection != null) {
					if (orderDirection.trim().equals("ASC")) {
						cursor = cursor.sort(new BsonDocument("recordTime", new BsonInt32(1)));
					} else if (orderDirection.trim().equals("DESC")) {
						cursor = cursor.sort(new BsonDocument("recordTime", new BsonInt32(-1)));
					}
				} else {
					cursor = cursor.sort(new BsonDocument("recordTime", new BsonInt32(-1)));
				}
			} else {
				if (orderDirection != null) {
					if (orderDirection.trim().equals("ASC")) {
						cursor = cursor.sort(new BsonDocument("any." + orderBy, new BsonInt32(1)));
					} else if (orderDirection.trim().equals("DESC")) {
						cursor = cursor.sort(new BsonDocument("any." + orderBy, new BsonInt32(-1)));
					}
				} else {
					cursor = cursor.sort(new BsonDocument("any." + orderBy, new BsonInt32(-1)));
				}
			}
		}

		/**
		 * eventCountLimit: If specified, the results will only include the
		 * first N events that match the other criteria, where N is the value of
		 * this parameter. The ordering specified by the orderBy and
		 * orderDirection parameters determine the meaning of "first" for this
		 * purpose. If omitted, all events matching the specified criteria will
		 * be included in the results. This parameter and maxEventCount are
		 * mutually exclusive; if both are specified, a QueryParameterException
		 * SHALL be raised. This parameter may only be used when orderBy is
		 * specified; if orderBy is omitted and eventCountLimit is specified, a
		 * QueryParameterException SHALL be raised. This parameter differs from
		 * maxEventCount in that this parameter limits the amount of data
		 * returned, whereas maxEventCount causes an exception to be thrown if
		 * the limit is exceeded.
		 */
		if (eventCountLimit != null) {
			try {
				cursor = cursor.limit(eventCountLimit);
			} catch (NumberFormatException nfe) {
				Configuration.logger.log(Level.ERROR, nfe.toString());
			}
		}

		return cursor;
	}

	private BsonArray makeQueryObjects(PollParameters p, String userID, List<String> friendList) {

		BsonArray queryList = new BsonArray();

		/**
		 * eventType : If specified, the result will only include events whose
		 * type matches one of the types specified in the parameter value. Each
		 * element of the parameter value may be one of the following strings:
		 * ObjectEvent, AggregationEvent, QuantityEvent, TransactionEvent, or
		 * TransformationEvent. An element of the parameter value may also be
		 * the name of an extension event type. If omitted, all event types will
		 * be considered for inclusion in the result.
		 *
		 * List of String CSV REGEX
		 */
		if (p.getEventType() != null) {
			BsonArray paramArray = getParamBsonArray(p.getEventType());
			BsonDocument queryObject = getQueryObject(new String[] { "eventType" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * GE_eventTime: If specified, only events with eventTime greater than
		 * or equal to the specified value will be included in the result. If
		 * omitted, events are included regardless of their eventTime (unless
		 * constrained by the LT_ eventTime parameter). Example:
		 * 2014-08-11T19:57:59.717+09:00 SimpleDateFormat sdf = new
		 * SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
		 * eventTime.setTime(sdf.parse(timeString)); e.g.
		 * 1988-07-04T12:08:56.235-07:00
		 *
		 * Verified
		 */
		if (p.getGE_eventTime() != null) {
			BsonDateTime geBsonDateTime = getTimeMillis(p.getGE_eventTime());
			BsonDocument query = new BsonDocument();
			query.put("eventTime", new BsonDocument("$gte", geBsonDateTime));
			queryList.add(query);
		}
		/**
		 * LT_eventTime: If specified, only events with eventTime less than the
		 * specified value will be included in the result. If omitted, events
		 * are included regardless of their eventTime (unless constrained by the
		 * GE_ eventTime parameter).
		 *
		 * Verified
		 */
		if (p.getLT_eventTime() != null) {
			BsonDateTime ltBsonDateTime = getTimeMillis(p.getLT_eventTime());
			BsonDocument query = new BsonDocument();
			query.put("eventTime", new BsonDocument("$lt", ltBsonDateTime));
			queryList.add(query);
		}
		/**
		 * GE_recordTime: If provided, only events with recordTime greater than
		 * or equal to the specified value will be returned. The automatic
		 * limitation based on event record time (Section 8.2.5.2) may
		 * implicitly provide a constraint similar to this parameter. If
		 * omitted, events are included regardless of their recordTime , other
		 * than automatic limitation based on event record time (Section
		 * 8.2.5.2).
		 *
		 * Verified
		 */
		if (p.getGE_recordTime() != null) {
			BsonDateTime geBsonDateTime = getTimeMillis(p.getGE_recordTime());
			BsonDocument query = new BsonDocument();
			query.put("recordTime", new BsonDocument("$gte", geBsonDateTime));
			queryList.add(query);
		}
		/**
		 * LE_recordTime: If provided, only events with recordTime less than the
		 * specified value will be returned. If omitted, events are included
		 * regardless of their recordTime (unless constrained by the GE_
		 * recordTime parameter or the automatic limitation based on event
		 * record time).
		 *
		 * Verified
		 */
		if (p.getLT_recordTime() != null) {
			BsonDateTime ltBsonDateTime = getTimeMillis(p.getLT_recordTime());
			BsonDocument query = new BsonDocument();
			query.put("recordTime", new BsonDocument("$lt", ltBsonDateTime));
			queryList.add(query);
		}

		/**
		 * GE_errorDeclaration Time: If this parameter is specified, the result
		 * will only include events that (a) contain an ErrorDeclaration ; and
		 * where (b) the value of the errorDeclarationTime field is greater than
		 * or equal to the specified value. If this parameter is omitted, events
		 * are returned regardless of whether they contain an ErrorDeclaration
		 * or what the value of the errorDeclarationTime field is.
		 */
		if (p.getGE_errorDeclarationTime() != null) {
			BsonDateTime geBsonDateTime = getTimeMillis(p.getGE_errorDeclarationTime());
			BsonDocument query = new BsonDocument();
			query.put("errorDeclaration.declarationTime", new BsonDocument("$gte", geBsonDateTime));
			queryList.add(query);
		}

		/**
		 * LT_errorDeclaration Time: contain an ErrorDeclaration ; and where (b)
		 * the value of the errorDeclarationTime field is less than to the
		 * specified value. If this parameter is omitted, events are returned
		 * regardless of whether they contain an ErrorDeclaration or what the
		 * value of the errorDeclarationTime field is.
		 */
		if (p.getLT_errorDeclarationTime() != null) {
			BsonDateTime ltBsonDateTime = getTimeMillis(p.getLT_errorDeclarationTime());
			BsonDocument query = new BsonDocument();
			query.put("errorDeclaration.declarationTime", new BsonDocument("$lt", ltBsonDateTime));
			queryList.add(query);
		}

		/**
		 * EQ_action: If specified, the result will only include events that (a)
		 * have an action field; and where (b) the value of the action field
		 * matches one of the specified values. The elements of the value of
		 * this parameter each must be one of the strings ADD , OBSERVE , or
		 * DELETE ; if not, the implementation SHALL raise a
		 * QueryParameterException . If omitted, events are included regardless
		 * of their action field.
		 *
		 * OR semantic
		 *
		 * Verified
		 */
		if (p.getEQ_action() != null) {
			// Constrained already checked
			BsonArray paramArray = getParamBsonArray(p.getEQ_action());
			BsonDocument queryObject = getQueryObject(new String[] { "action" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}
		/**
		 * EQ_bizStep: If specified, the result will only include events that
		 * (a) have a non-null bizStep field; and where (b) the value of the
		 * bizStep field matches one of the specified values. If this parameter
		 * is omitted, events are returned regardless of the value of the
		 * bizStep field or whether the bizStep field exists at all.
		 *
		 * OR semantic Regex supported
		 *
		 * Verified
		 */
		if (p.getEQ_bizStep() != null) {
			BsonArray paramArray = getParamBsonArray(p.getEQ_bizStep());
			BsonDocument queryObject = getQueryObject(new String[] { "bizStep" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}
		/**
		 * Like the EQ_ bizStep parameter, but for the disposition field.
		 *
		 * OR semantic Regex Supported
		 *
		 * Verified
		 */
		if (p.getEQ_disposition() != null) {
			BsonArray paramArray = getParamBsonArray(p.getEQ_disposition());
			BsonDocument queryObject = getQueryObject(new String[] { "disposition" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}
		/**
		 * EQ_readPoint: If specified, the result will only include events that
		 * (a) have a non-null readPoint field; and where (b) the value of the
		 * readPoint field matches one of the specified values. If this
		 * parameter and WD_ readPoint are both omitted, events are returned
		 * regardless of the value of the readPoint field or whether the
		 * readPoint field exists at all.
		 *
		 * OR semantic Regex supported
		 *
		 */
		if (p.getEQ_readPoint() != null) {
			BsonArray paramArray = getParamBsonArray(p.getEQ_readPoint());
			BsonDocument queryObject = getQueryObject(new String[] { "readPoint.id" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * WD_readPoint: If specified, the result will only include events that
		 * (a) have a non-null readPoint field; and where (b) the value of the
		 * readPoint field matches one of the specified values, or is a direct
		 * or indirect descendant of one of the specified values. The meaning of
		 * "direct or indirect descendant" is specified by master data, as
		 * described in Section 6.5. (WD is an abbreviation for "with
		 * descendants.") If this parameter and EQ_readPoint are both omitted,
		 * events are returned regardless of the value of the readPoint field or
		 * whether the readPoint field exists at all.
		 *
		 * OR semantic Regex Supported
		 *
		 */

		if (p.getWD_readPoint() != null) {
			BsonArray paramArray = getWDParamBsonArray(p.getWD_readPoint());
			BsonDocument queryObject = getQueryObject(new String[] { "readPoint.id" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * EQ_bizLocation: Like the EQ_ readPoint parameter, but for the
		 * bizLocation field.
		 *
		 * OR semantic Regex Supported
		 *
		 */
		if (p.getEQ_bizLocation() != null) {
			BsonArray paramArray = getParamBsonArray(p.getEQ_bizLocation());
			BsonDocument queryObject = getQueryObject(new String[] { "bizLocation.id" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * WD_bizLocation: Like the WD_readPoint parameter, but for the
		 * bizLocation field.
		 *
		 * OR semantic Regex Supported
		 *
		 */

		if (p.getWD_bizLocation() != null) {
			BsonArray paramArray = getWDParamBsonArray(p.getWD_bizLocation());
			BsonDocument queryObject = getQueryObject(new String[] { "bizLocation.id" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * EQ_transformationID: If this parameter is specified, the result will
		 * only include events that (a) have a transformationID field (that is,
		 * TransformationEvents or extension event type that extend
		 * TransformationEvent); and where (b) the transformationID field is
		 * equal to one of the values specified in this parameter.
		 *
		 * OR semantic Regex Supported
		 *
		 */
		if (p.getEQ_transformationID() != null) {
			BsonArray paramArray = getParamBsonArray(p.getEQ_transformationID());
			BsonDocument queryObject = getQueryObject(new String[] { "transformationID" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * MATCH_epc: If this parameter is specified, the result will only
		 * include events that (a) have an epcList or a childEPCs field (that
		 * is, ObjectEvent, AggregationEvent, TransactionEvent or extension
		 * event types that extend one of those three); and where (b) one of the
		 * EPCs listed in the epcList or childEPCs field (depending on event
		 * type) matches one of the EPC patterns or URIs specified in this
		 * parameter, where the meaning of "matches" is as specified in Section
		 * 8.2.7.1.1. If this parameter is omitted, events are included
		 * regardless of their epcList or childEPCs field or whether the epcList
		 * or childEPCs field exists.
		 *
		 *
		 */
		if (p.getMATCH_epc() != null) {
			BsonArray paramArray = getParamBsonArray(p.getMATCH_epc());
			BsonDocument queryObject = getMatchQueryObject(new String[] { "epcList.epc", "childEPCs.epc" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * MATCH_parentID: Like MATCH_epc, but matches the parentID field of
		 * AggregationEvent, the parentID field of TransactionEvent, and
		 * extension event types that extend either AggregationEvent or
		 * TransactionEvent. The meaning of "matches" is as specified in Section
		 * 8.2.7.1.1.
		 */
		if (p.getMATCH_parentID() != null) {
			BsonArray paramArray = getParamBsonArray(p.getMATCH_parentID());
			BsonDocument queryObject = getMatchQueryObject(new String[] { "parentID" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * MATCH_inputEPC: If this parameter is specified, the result will only
		 * include events that (a) have an inputEPCList (that is,
		 * TransformationEvent or an extension event type that extends
		 * TransformationEvent); and where (b) one of the EPCs listed in the
		 * inputEPCList field matches one of the EPC patterns or URIs specified
		 * in this parameter. The meaning of "matches" is as specified in
		 * Section 8.2.7.1.1. If this parameter is omitted, events are included
		 * regardless of their inputEPCList field or whether the inputEPCList
		 * field exists.
		 */
		if (p.getMATCH_inputEPC() != null) {
			BsonArray paramArray = getParamBsonArray(p.getMATCH_inputEPC());
			BsonDocument queryObject = getMatchQueryObject(new String[] { "inputEPCList.epc" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * MATCH_outputEPC: If this parameter is specified, the result will only
		 * include events that (a) have an inputEPCList (that is,
		 * TransformationEvent or an extension event type that extends
		 * TransformationEvent); and where (b) one of the EPCs listed in the
		 * inputEPCList field matches one of the EPC patterns or URIs specified
		 * in this parameter. The meaning of "matches" is as specified in
		 * Section 8.2.7.1.1. If this parameter is omitted, events are included
		 * regardless of their inputEPCList field or whether the inputEPCList
		 * field exists.
		 */
		if (p.getMATCH_outputEPC() != null) {
			BsonArray paramArray = getParamBsonArray(p.getMATCH_outputEPC());
			BsonDocument queryObject = getMatchQueryObject(new String[] { "outputEPCList.epc" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * MATCH_anyEPC: If this parameter is specified, the result will only
		 * include events that (a) have an epcList field, a childEPCs field, a
		 * parentID field, an inputEPCList field, or an outputEPCList field
		 * (that is, ObjectEvent, AggregationEvent, TransactionEvent,
		 * TransformationEvent, or extension event types that extend one of
		 * those four); and where (b) the parentID field or one of the EPCs
		 * listed in the epcList, childEPCs, inputEPCList, or outputEPCList
		 * field (depending on event type) matches one of the EPC patterns or
		 * URIs specified in this parameter. The meaning of "matches" is as
		 * specified in Section 8.2.7.1.1.
		 */

		if (p.getMATCH_anyEPC() != null) {
			BsonArray paramArray = getParamBsonArray(p.getMATCH_anyEPC());
			BsonDocument queryObject = getMatchQueryObject(new String[] { "epcList.epc", "childEPCs.epc",
					"inputEPCList.epc", "outputEPCList.epc", "parentID" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * MATCH_epcClass: If this parameter is specified, the result will only
		 * include events that (a) have a quantityList or a childQuantityList
		 * field (that is, ObjectEvent, AggregationEvent, TransactionEvent or
		 * extension event types that extend one of those three); and where (b)
		 * one of the EPC classes listed in the quantityList or
		 * childQuantityList field (depending on event type) matches one of the
		 * EPC patterns or URIs specified in this parameter. The result will
		 * also include QuantityEvents whose epcClass field matches one of the
		 * EPC patterns or URIs specified in this parameter. The meaning of
		 * "matches" is as specified in Section 8.2.7.1.1.
		 */
		if (p.getMATCH_epcClass() != null) {

			BsonArray paramArray = getParamBsonArray(p.getMATCH_epcClass());
			BsonDocument queryObject = getMatchQueryObject(
					new String[] { "extension.quantityList.epcClass", "extension.childQuantityList.epcClass" },
					paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * MATCH_inputEPCClass: If this parameter is specified, the result will
		 * only include events that (a) have an inputQuantityList field (that
		 * is, TransformationEvent or extension event types that extend it); and
		 * where (b) one of the EPC classes listed in the inputQuantityList
		 * field (depending on event type) matches one of the EPC patterns or
		 * URIs specified in this parameter. The meaning of "matches" is as
		 * specified in Section 8.2.7.1.1.
		 */
		if (p.getMATCH_inputEPCClass() != null) {
			BsonArray paramArray = getParamBsonArray(p.getMATCH_inputEPCClass());
			BsonDocument queryObject = getMatchQueryObject(new String[] { "inputQuantityList.epcClass" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * MATCH_outputEPCClass: If this parameter is specified, the result will
		 * only include events that (a) have an outputQuantityList field (that
		 * is, TransformationEvent or extension event types that extend it); and
		 * where (b) one of the EPC classes listed in the outputQuantityList
		 * field (depending on event type) matches one of the EPC patterns or
		 * URIs specified in this parameter. The meaning of "matches" is as
		 * specified in Section 8.2.7.1.1.
		 */

		if (p.getMATCH_outputEPCClass() != null) {
			BsonArray paramArray = getParamBsonArray(p.getMATCH_outputEPCClass());
			BsonDocument queryObject = getMatchQueryObject(new String[] { "outputQuantityList.epcClass" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * MATCH_anyEPCClass: If this parameter is specified, the result will
		 * only include events that (a) have a quantityList, childQuantityList,
		 * inputQuantityList, or outputQuantityList field (that is, ObjectEvent,
		 * AggregationEvent, TransactionEvent, TransformationEvent, or extension
		 * event types that extend one of those four); and where (b) one of the
		 * EPC classes listed in any of those fields matches one of the EPC
		 * patterns or URIs specified in this parameter. The result will also
		 * include QuantityEvents whose epcClass field matches one of the EPC
		 * patterns or URIs specified in this parameter. The meaning of
		 * "matches" is as specified in Section 8.2.7.1.1.
		 */
		if (p.getMATCH_anyEPCClass() != null) {
			BsonArray paramArray = getParamBsonArray(p.getMATCH_anyEPCClass());
			BsonDocument queryObject = getMatchQueryObject(
					new String[] { "extension.quantityList.epcClass", "extension.childQuantityList.epcClass",
							"inputQuantityList.epcClass", "outputQuantityList.epcClass" },
					paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * (DEPCRECATED in EPCIS 1.1) EQ_quantity; GT_quantity; GE_quantity;
		 * LT_quantity; LE_quantity
		 **/

		/**
		 * EQ_eventID : If this parameter is specified, the result will only
		 * include events that (a) have a non-null eventID field; and where (b)
		 * the eventID field is equal to one of the values specified in this
		 * parameter. If this parameter is omitted, events are returned
		 * regardless of the value of the eventID field or whether the eventID
		 * field exists at all.
		 *
		 * List of String
		 *
		 */
		if (p.getEQ_eventID() != null) {

			BsonArray orQueryArray = new BsonArray();
			BsonArray paramArray = getParamBsonArray(p.getEQ_eventID());
			BsonDocument queryObject = getQueryObject(new String[] { "eventID" }, paramArray);
			if (queryObject != null) {
				orQueryArray.add(queryObject);
			}
			BsonArray objectIDParamArray = new BsonArray();
			for (int i = 0; i < paramArray.size(); i++) {
				BsonValue paramValue = paramArray.get(i);
				if (paramValue instanceof BsonString) {
					try {
						objectIDParamArray.add(new BsonObjectId(new ObjectId(paramValue.asString().getValue())));
					} catch (IllegalArgumentException e) {
						Configuration.logger.debug("Non MongoDB ObjectID: " + e.toString());
					}
				}
			}
			BsonDocument objectIDQueryObject = getQueryObject(new String[] { "_id" }, objectIDParamArray);
			if (objectIDQueryObject != null) {
				orQueryArray.add(objectIDQueryObject);
			}
			if (orQueryArray.size() != 0) {
				BsonDocument orQueryObject = new BsonDocument();
				orQueryObject.put("$or", orQueryArray);
				queryList.add(orQueryObject);
			}

		}

		/**
		 * EQ_errorReason: If this parameter is specified, the result will only
		 * include events that (a) contain an ErrorDeclaration ; and where (b)
		 * the error declaration contains a non-null reason field; and where (c)
		 * the reason field is equal to one of the values specified in this
		 * parameter. If this parameter is omitted, events are returned
		 * regardless of the they contain an ErrorDeclaration or what the value
		 * of the reason field is.
		 */

		if (p.getEQ_errorReason() != null) {
			BsonArray paramArray = getParamBsonArray(p.getEQ_errorReason());
			BsonDocument queryObject = getQueryObject(new String[] { "errorDeclaration.reason" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * EQ_correctiveEventID: If this parameter is specified, the result will
		 * only include events that (a) contain an ErrorDeclaration ; and where
		 * (b) one of the elements of the correctiveEventIDs list is equal to
		 * one of the values specified in this parameter. If this parameter is
		 * omitted, events are returned regardless of the they contain an
		 * ErrorDeclaration or the contents of the correctiveEventIDs list.
		 */

		if (p.getEQ_correctiveEventID() != null) {
			BsonArray paramArray = getParamBsonArray(p.getEQ_correctiveEventID());
			BsonDocument queryObject = getQueryObject(new String[] { "errorDeclaration.correctiveEventIDs" },
					paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * EXISTS_errorDeclaration: If this parameter is specified, the result
		 * will only include events that contain an ErrorDeclaration . If this
		 * parameter is omitted, events are returned regardless of whether they
		 * contain an ErrorDeclaration .
		 */

		if (p.getEXISTS_errorDeclaration() != null) {

			Boolean isExist = Boolean.parseBoolean(p.getEXISTS_errorDeclaration().toString());
			BsonBoolean isExistBson = new BsonBoolean(isExist);
			BsonDocument query = getExistsQueryObject("errorDeclaration", null, isExistBson);
			if (query != null)
				queryList.add(query);
		}

		if (p.getParams() != null) {
			Iterator<String> paramIter = p.getParams().keySet().iterator();
			while (paramIter.hasNext()) {
				String paramName = paramIter.next();
				String paramValues = p.getParams().get(paramName);

				/**
				 * EQ_bizTransaction_type: This is not a single parameter, but a
				 * family of parameters. If a parameter of this form is
				 * specified, the result will only include events that (a)
				 * include a bizTransactionList; (b) where the business
				 * transaction list includes an entry whose type subfield is
				 * equal to type extracted from the name of this parameter; and
				 * (c) where the bizTransaction subfield of that entry is equal
				 * to one of the values specified in this parameter.
				 */
				if (paramName.contains("EQ_bizTransaction_")) {
					String type = paramName.substring(18, paramName.length());
					type = MongoWriterUtil.encodeMongoObjectKey(type);
					BsonDocument query = getFamilyQueryObject(type, new String[] { "bizTransactionList" }, paramValues);
					if (query != null)
						queryList.add(query);
				}

				/**
				 * EQ_source_type: This is not a single parameter, but a family
				 * of parameters. If a parameter of this form is specified, the
				 * result will only include events that (a) include a
				 * sourceList; (b) where the source list includes an entry whose
				 * type subfield is equal to type extracted from the name of
				 * this parameter; and (c) where the source subfield of that
				 * entry is equal to one of the values specified in this
				 * parameter.
				 */

				if (paramName.contains("EQ_source_")) {
					String type = paramName.substring(10, paramName.length());
					type = MongoWriterUtil.encodeMongoObjectKey(type);
					/*
					 * if (eventType.equals("AggregationEvent") ||
					 * eventType.equals("ObjectEvent") ||
					 * eventType.equals("TransactionEvent")) { BsonDocument
					 * query = getFamilyQueryObject(type,
					 * "extension.sourceList", paramValues); if (query != null)
					 * queryList.add(query); } if
					 * (eventType.equals("TransformationEvent")) { BsonDocument
					 * query = getFamilyQueryObject(type, "sourceList",
					 * paramValues); if (query != null) queryList.add(query); }
					 */
					/*
					 * if (eventType.equals("AggregationEvent") ||
					 * eventType.equals("ObjectEvent") ||
					 * eventType.equals("TransactionEvent")) {
					 *
					 * } if (eventType.equals("TransformationEvent")) {
					 * BsonDocument query = getFamilyQueryObject(type,
					 * "sourceList", paramValues); if (query != null)
					 * queryList.add(query); }
					 */
					BsonDocument query = getFamilyQueryObject(type,
							new String[] { "extension.sourceList", "sourceList" }, paramValues);
					if (query != null)
						queryList.add(query);
				}

				/**
				 * EQ_destination_type: This is not a single parameter, but a
				 * family of parameters. If a parameter of this form is
				 * specified, the result will only include events that (a)
				 * include a destinationList; (b) where the destination list
				 * includes an entry whose type subfield is equal to type
				 * extracted from the name of this parameter; and (c) where the
				 * destination subfield of that entry is equal to one of the
				 * values specified in this parameter.
				 */
				if (paramName.contains("EQ_destination_")) {
					String type = paramName.substring(15, paramName.length());
					type = MongoWriterUtil.encodeMongoObjectKey(type);
					/*
					 * if (eventType.equals("AggregationEvent") ||
					 * eventType.equals("ObjectEvent") ||
					 * eventType.equals("TransactionEvent")) { BsonDocument
					 * query = getFamilyQueryObject(type,
					 * "extension.destinationList", paramValues); if (query !=
					 * null) queryList.add(query); } if
					 * (eventType.equals("TransformationEvent")) { BsonDocument
					 * query = getFamilyQueryObject(type, "destinationList",
					 * paramValues); if (query != null) queryList.add(query); }
					 */
					BsonDocument query = getFamilyQueryObject(type,
							new String[] { "extension.destinationList", "destinationList" }, paramValues);
					if (query != null)
						queryList.add(query);
				}

				/**
				 * EQ_ILMD_field: Analogous to EQ_fieldname , but matches events
				 * whose ILMD area (Section 7.3.6) contains a top-level field
				 * having the specified fieldname whose value matches one of the
				 * specified values. "Top level" means that the matching ILMD
				 * element must be an immediate child of the <ilmd> element, not
				 * an element nested within such an element. See
				 * EQ_INNER_ILMD_fieldname for querying inner extension
				 * elements.
				 */

				if (paramName.startsWith("EQ_ILMD_")) {
					String type = paramName.substring(8, paramName.length());
					type = MongoWriterUtil.encodeMongoObjectKey(type);
					BsonArray paramArray = getParamBsonArray(paramValues);
					BsonDocument queryObject = getQueryObject(
							new String[] { "extension.ilmd.any." + type, "ilmd.any." + type }, paramArray);
					if (queryObject != null) {
						queryList.add(queryObject);
					}
				}

				/**
				 * GT|GE|LT|LE_ILMD_field: Analogous to EQ_fieldname ,
				 * GT_fieldname , GE_fieldname , GE_fieldname , LT_fieldname ,
				 * and LE_fieldname , respectively, but matches events whose
				 * ILMD area (Section 7.3.6) contains a field having the
				 * specified fieldname whose integer, float, or time value
				 * matches the specified value according to the specified
				 * relational operator.
				 */

				if (paramName.startsWith("GT_ILMD_") || paramName.startsWith("GE_ILMD_")
						|| paramName.startsWith("LT_ILMD_") || paramName.startsWith("LE_ILMD_")) {
					String type = paramName.substring(8, paramName.length());
					type = MongoWriterUtil.encodeMongoObjectKey(type);

					if (paramName.startsWith("GT_")) {
						BsonDocument query = getCompExtensionQueryObject(type,
								new String[] { "extension.ilmd.any." + type, "ilmd.any." + type }, paramValues, "GT");
						if (query != null)
							queryList.add(query);
					}
					if (paramName.startsWith("GE_")) {
						BsonDocument query = getCompExtensionQueryObject(type,
								new String[] { "extension.ilmd.any." + type, "ilmd.any." + type }, paramValues, "GE");
						if (query != null)
							queryList.add(query);
					}
					if (paramName.startsWith("LT_")) {
						BsonDocument query = getCompExtensionQueryObject(type,
								new String[] { "extension.ilmd.any." + type, "ilmd.any." + type }, paramValues, "LT");
						if (query != null)
							queryList.add(query);
					}
					if (paramName.startsWith("LE_")) {
						BsonDocument query = getCompExtensionQueryObject(type,
								new String[] { "extension.ilmd.any." + type, "ilmd.any." + type }, paramValues, "LE");
						if (query != null)
							queryList.add(query);
					}
				}

				/**
				 * EXISTS_ILMD_fieldname: Like EXISTS_fieldname as described
				 * above, but events that have a non-empty field named fieldname
				 * in the ILMD area (Section 7.3.6). Fieldname is constructed as
				 * for EQ_ILMD_fieldname . Note that the value for this query
				 * parameter is ignored.
				 */
				if (paramName.startsWith("EXISTS_ILMD_")) {
					/*
					 * if (p.getEventType().equals("ObjectEvent")) { String
					 * field = paramName.substring(12, paramName.length());
					 * field = MongoWriterUtil.encodeMongoObjectKey(field);
					 * Boolean isExist = Boolean.parseBoolean(paramValues);
					 * BsonBoolean isExistBson = new BsonBoolean(isExist);
					 * BsonDocument query =
					 * getExistsQueryObject("extension.ilmd", field,
					 * isExistBson); if (query != null) queryList.add(query); }
					 * else if (p.getEventType().equals("TransformationEvent"))
					 * { String field = paramName.substring(12,
					 * paramName.length()); field =
					 * MongoWriterUtil.encodeMongoObjectKey(field); Boolean
					 * isExist = Boolean.parseBoolean(paramValues); BsonBoolean
					 * isExistBson = new BsonBoolean(isExist); BsonDocument
					 * query = getExistsQueryObject("ilmd", field, isExistBson);
					 * if (query != null) queryList.add(query); }
					 */

					String field = paramName.substring(12, paramName.length());
					field = MongoWriterUtil.encodeMongoObjectKey(field);
					Boolean isExist = Boolean.parseBoolean(paramValues);
					BsonBoolean isExistBson = new BsonBoolean(isExist);
					BsonDocument query = getExistsQueryObject(new String[] { "extension.ilmd.any", "ilmd.any" }, field,
							isExistBson);
					if (query != null)
						queryList.add(query);
				}

				/**
				 * EQ_ERROR_DECLARATION_Fieldname : Analogous to EQ_fieldname ,
				 * but matches events containing an ErrorDeclaration and where
				 * the ErrorDeclaration contains a field having the specified
				 * fieldname whose value matches one of the specified values.
				 *
				 * List of String
				 *
				 */

				if (paramName.startsWith("EQ_ERROR_DECLARATION_")) {
					String type = paramName.substring(21, paramName.length());
					type = MongoWriterUtil.encodeMongoObjectKey(type);

					BsonArray paramArray = getParamBsonArray(paramValues);
					BsonDocument queryObject = getQueryObject(new String[] { "errorDeclaration.any." + type },
							paramArray);
					if (queryObject != null) {
						queryList.add(queryObject);
					}
				}

				/**
				 * Analogous to EQ_fieldname , GT_fieldname , GE_fieldname ,
				 * GE_fieldname , LT_fieldname , and LE_fieldname ,
				 * respectively, but matches events containing an
				 * ErrorDeclaration and where the ErrorDeclaration contains a
				 * field having the specified fieldname whose integer, float, or
				 * time value matches the specified value according to the
				 * specified relational operator.
				 */

				if (paramName.startsWith("GT_ERROR_DECLARATION_") || paramName.startsWith("GE_ERROR_DECLARATION_")
						|| paramName.startsWith("LT_ERROR_DECLARATION_")
						|| paramName.startsWith("LE_ERROR_DECLARATION_")) {
					String type = paramName.substring(21, paramName.length());
					type = MongoWriterUtil.encodeMongoObjectKey(type);

					if (paramName.startsWith("GT_")) {
						BsonDocument query = getCompExtensionQueryObject(type,
								new String[] { "errorDeclaration.any." + type }, paramValues, "GT");
						if (query != null)
							queryList.add(query);
					}
					if (paramName.startsWith("GE_")) {
						BsonDocument query = getCompExtensionQueryObject(type,
								new String[] { "errorDeclaration.any." + type }, paramValues, "GE");
						if (query != null)
							queryList.add(query);
					}
					if (paramName.startsWith("LT_")) {
						BsonDocument query = getCompExtensionQueryObject(type,
								new String[] { "errorDeclaration.any." + type }, paramValues, "LT");
						if (query != null)
							queryList.add(query);
					}
					if (paramName.startsWith("LE_")) {
						BsonDocument query = getCompExtensionQueryObject(type,
								new String[] { "errorDeclaration.any." + type }, paramValues, "LE");
						if (query != null)
							queryList.add(query);
					}
				}

				boolean isExtraParam = isExtraParameter(paramName);

				if (isExtraParam == true) {

					/**
					 * EQ_fieldname: This is not a single parameter, but a
					 * family of parameters. If a parameter of this form is
					 * specified, the result will only include events that (a)
					 * have a field named fieldname whose type is either String
					 * or a vocabulary type; and where (b) the value of that
					 * field matches one of the values specified in this
					 * parameter. Fieldname is the fully qualified name of an
					 * extension field. The name of an extension field is an XML
					 * qname; that is, a pair consisting of an XML namespace URI
					 * and a name. The name of the corresponding query parameter
					 * is constructed by concatenating the following: the string
					 * EQ_, the namespace URI for the extension field, a pound
					 * sign (#), and the name of the extension field.
					 */
					if (paramName.startsWith("EQ_")) {
						String type = paramName.substring(3, paramName.length());
						type = MongoWriterUtil.encodeMongoObjectKey(type);

						BsonArray paramArray = getParamBsonArray(paramValues);
						BsonDocument queryObject = getQueryObject(
								new String[] { "any." + type, "otherAttributes." + type }, paramArray);
						if (queryObject != null) {
							queryList.add(queryObject);
						}
					}

					/**
					 * GT/GE/LT/LE_fieldname: Like EQ_fieldname as described
					 * above, but may be applied to a field of type Int, Float,
					 * or Time. The result will include events that (a) have a
					 * field named fieldname; and where (b) the type of the
					 * field matches the type of this parameter (Int, Float, or
					 * Time); and where (c) the value of the field is greater
					 * than the specified value. Fieldname is constructed as for
					 * EQ_fieldname.
					 */

					if (paramName.startsWith("GT_") || paramName.startsWith("GE_") || paramName.startsWith("LT_")
							|| paramName.startsWith("LE_")) {
						String type = paramName.substring(3, paramName.length());
						type = MongoWriterUtil.encodeMongoObjectKey(type);

						if (paramName.startsWith("GT_")) {
							BsonDocument query = getCompExtensionQueryObject(type,
									new String[] { "any." + type, "otherAttributes." + type }, paramValues, "GT");
							if (query != null)
								queryList.add(query);
						}
						if (paramName.startsWith("GE_")) {
							BsonDocument query = getCompExtensionQueryObject(type,
									new String[] { "any." + type, "otherAttributes." + type }, paramValues, "GE");
							if (query != null)
								queryList.add(query);
						}
						if (paramName.startsWith("LT_")) {
							BsonDocument query = getCompExtensionQueryObject(type,
									new String[] { "any." + type, "otherAttributes." + type }, paramValues, "LT");
							if (query != null)
								queryList.add(query);
						}
						if (paramName.startsWith("LE_")) {
							BsonDocument query = getCompExtensionQueryObject(type,
									new String[] { "any." + type, "otherAttributes." + type }, paramValues, "LE");
							if (query != null)
								queryList.add(query);
						}
					}

					/**
					 * EXISTS_fieldname: Like EQ_fieldname as described above,
					 * but may be applied to a field of any type (including
					 * complex types). The result will include events that have
					 * a non-empty field named fieldname . Fieldname is
					 * constructed as for EQ_fieldname . EXISTS_ ILMD_fieldname
					 * HASATTR_fieldname Void Note that the value for this query
					 * parameter is ignored.
					 *
					 * Regex not supported
					 *
					 */

					if (paramName.startsWith("EXISTS_")) {

						String field = paramName.substring(7, paramName.length());
						field = MongoWriterUtil.encodeMongoObjectKey(field);
						paramValues = MongoWriterUtil.encodeMongoObjectKey(paramValues);
						Boolean isExist = Boolean.parseBoolean(paramValues);
						BsonBoolean isExistBson = new BsonBoolean(isExist);
						BsonDocument query = getExistsQueryObject("any", field, isExistBson);
						if (query != null)
							queryList.add(query);
					}
				}
			}
		}
		return queryList;
	}

	@SuppressWarnings("unused")
	private boolean isPostFilterPassed(String eventType, BsonDocument dbObject, Map<String, String> paramMap) {
		if (paramMap == null || paramMap.isEmpty())
			return true;
		Iterator<String> paramIter = paramMap.keySet().iterator();
		while (paramIter.hasNext()) {
			String paramName = paramIter.next();
			String paramValues = paramMap.get(paramName);

			BsonDocument ilmd = null;
			BsonDocument error = null;
			BsonDocument ext = null;

			// Prepare BsonDocument
			if (eventType.equals("ObjectEvent")) {
				if (dbObject.containsKey("extension") && dbObject.getDocument("extension").containsKey("ilmd")
						&& dbObject.getDocument("extension").getDocument("ilmd").containsKey("any")) {
					ilmd = dbObject.getDocument("extension").getDocument("ilmd").getDocument("any");
				}
			} else if (eventType.equals("TransformationEvent")) {
				if (dbObject.containsKey("ilmd") && dbObject.getDocument("ilmd").containsKey("any")) {
					ilmd = dbObject.getDocument("ilmd").getDocument("any");
				}
			}

			if (dbObject.containsKey("errorDeclaration")) {
				if (dbObject.getDocument("errorDeclaration").containsKey("any")) {
					error = dbObject.getDocument("errorDeclaration").getDocument("any");
				}
			}

			if (dbObject.containsKey("any")) {
				ext = dbObject.getDocument("any");
			}

			// TODO: HASATTR_fieldname

			/**
			 * HASATTR_fieldname: This is not a single parameter, but a family
			 * of parameters. If a parameter of this form is specified, the
			 * result will only include events that (a) have a field named
			 * fieldname whose type is a vocabulary type; and (b) where the
			 * value of that field is a vocabulary element for which master data
			 * is available; and (c) the master data has a non-null attribute
			 * whose name matches one of the values specified in this parameter.
			 * Fieldname is the fully qualified name of a field. For a standard
			 * field, this is simply the field name; e.g., bizLocation . For an
			 * extension EQATTR_fieldname _attrname List of String field, the
			 * name of an extension field is an XML qname; that is, a pair
			 * consisting of an XML namespace URI and a name. The name of the
			 * corresponding query parameter is constructed by concatenating the
			 * following: the string HASATTR_ , the namespace URI for the
			 * extension field, a pound sign (#), and the name of the extension
			 * field.
			 */

			if (paramName.startsWith("HASATTR_")) {
				String type = paramName.substring(8, paramName.length());
				type = MongoWriterUtil.encodeMongoObjectKey(type);

				BsonArray paramArray = getParamBsonArray(paramValues);

				continue;
			}

			// TODO: EQATTR_fieldname_attrname

			/**
			 * This is not a single parameter, but a family of parameters. If a
			 * parameter of this form is specified, the result will only include
			 * events that (a) have a field named fieldname whose type is a
			 * vocabulary type; and (b) where the value of that field is a
			 * vocabulary element for which master data is available; and (c)
			 * the master data has a non-null attribute named attrname ; and (d)
			 * where the value of that attribute matches one of the values
			 * specified in this parameter. Fieldname is constructed as for
			 * HASATTR_fieldname . The implementation MAY raise a
			 * QueryParameterException if fieldname or attrname includes an
			 * underscore character. EQ_eventID List of String EXISTS_
			 * errorDeclaration Void GE_errorDeclaration Time Time Explanation
			 * (non-normative): because the presence of an underscore in
			 * fieldname or attrname presents an ambiguity as to where the
			 * division between fieldname and attrname lies, an implementation
			 * is free to reject the query parameter if it cannot disambiguate.
			 */

			if (paramName.startsWith("EQATTR_")) {
				String type = paramName.substring(7, paramName.length());
				String[] typeArr = type.trim().split("_");
				if (typeArr.length != 2)
					continue;
				String fieldname = typeArr[0];
				String attrname = typeArr[1];

				BsonArray paramArray = getParamBsonArray(paramValues);

				continue;
			}

			/**
			 * Analogous to EQ_ILMD_fieldname , but matches inner ILMD elements;
			 * that is, any XML element nested within a top-level ILMD element.
			 * Note that a matching inner element may exist within in more than
			 * one top-level element or may occur more than once within a single
			 * top-level element; this parameter matches if at least one
			 * matching occurrence is found anywhere in the ILMD section (except
			 * at top-level).
			 */

			if (paramName.startsWith("EQ_INNER_ILMD_")) {
				if (eventType.equals("AggregationEvent") || eventType.equals("QuantityEvent")
						|| eventType.equals("TransactionEvent")) {
					return false;
				}
				if (ilmd == null)
					return false;
				String type = paramName.substring(14, paramName.length());
				BsonArray paramArray = getParamBsonArray(paramValues);

				if (isExtensionFilterPassed(type, paramArray, ilmd, true) == true)
					return true;
				else
					return false;
			}

			/**
			 * Like EQ_INNER_ILMD_ fieldname as described above, but may be
			 * applied to a field of type Int, Float, or Time.
			 */

			if (paramName.startsWith("GT_INNER_ILMD_") || paramName.startsWith("GE_INNER_ILMD_")
					|| paramName.startsWith("LT_INNER_ILMD_") || paramName.startsWith("LE_INNER_ILMD_")) {

				if (eventType.equals("AggregationEvent") || eventType.equals("QuantityEvent")
						|| eventType.equals("TransactionEvent")) {
					return false;
				}
				if (ilmd == null)
					return false;
				String type = paramName.substring(14, paramName.length());
				BsonArray paramArray = getParamBsonArray(paramValues);

				if (paramName.startsWith("GT_")) {
					if (isCompExtensionFilterPassed(type, "GT", paramArray, ilmd) == true)
						return true;
					else
						return false;
				}
				if (paramName.startsWith("GE_")) {
					if (isCompExtensionFilterPassed(type, "GE", paramArray, ilmd) == true)
						return true;
					else
						return false;
				}
				if (paramName.startsWith("LT_")) {
					if (isCompExtensionFilterPassed(type, "LT", paramArray, ilmd) == true)
						return true;
					else
						return false;
				}
				if (paramName.startsWith("LE_")) {
					if (isCompExtensionFilterPassed(type, "LE", paramArray, ilmd) == true)
						return true;
					else
						return false;
				}
			}

			/**
			 * Analogous to EQ_ERROR_DECLARATION_fieldname , but matches inner
			 * extension elements; that is, any XML element nested within a
			 * top-level extension element. Note that a matching inner element
			 * may exist within in more than one top-level element or may occur
			 * more than once within a single top-level element; this parameter
			 * matches if at least one matching occurrence is found anywhere in
			 * the event (except at top-level)..
			 */

			if (paramName.startsWith("EQ_INNER_ERROR_DECLARATION_")) {
				if (error == null)
					return false;
				String type = paramName.substring(27, paramName.length());
				BsonArray paramArray = getParamBsonArray(paramValues);

				if (isExtensionFilterPassed(type, paramArray, error, true) == true)
					return true;
				else
					return false;
			}

			/**
			 * Like EQ_INNER_ERROR_DECLARATION _ fieldname as described above,
			 * but may be applied to a field of type Int, Float, or Time.
			 */

			if (paramName.startsWith("GT_INNER_ERROR_DECLARATION_")
					|| paramName.startsWith("GE_INNER_ERROR_DECLARATION_")
					|| paramName.startsWith("LT_INNER_ERROR_DECLARATION_")
					|| paramName.startsWith("LE_INNER_ERROR_DECLARATION_")) {

				if (error == null)
					return false;
				String type = paramName.substring(27, paramName.length());
				BsonArray paramArray = getParamBsonArray(paramValues);

				if (paramName.startsWith("GT_")) {
					if (isCompExtensionFilterPassed(type, "GT", paramArray, error) == true)
						return true;
					else
						return false;
				}
				if (paramName.startsWith("GE_")) {
					if (isCompExtensionFilterPassed(type, "GE", paramArray, error) == true)
						return true;
					else
						return false;
				}
				if (paramName.startsWith("LT_")) {
					if (isCompExtensionFilterPassed(type, "LT", paramArray, error) == true)
						return true;
					else
						return false;
				}
				if (paramName.startsWith("LE_")) {
					if (isCompExtensionFilterPassed(type, "LE", paramArray, error) == true)
						return true;
					else
						return false;
				}
			}

			/**
			 * Analogous to EQ_fieldname , but matches inner extension elements;
			 * that is, any XML element nested within a top-level extension
			 * element. Note that a matching inner element may exist within in
			 * more than one top-level element or may occur more than once
			 * within a single top-level element; this parameter matches if at
			 * least one matching occurrence is found anywhere in the event
			 * (except at top-level).
			 */

			if (paramName.startsWith("EQ_INNER_")) {
				if (ext == null)
					return false;
				String type = paramName.substring(9, paramName.length());
				BsonArray paramArray = getParamBsonArray(paramValues);

				if (isExtensionFilterPassed(type, paramArray, ext, true) == true)
					return true;
				else
					return false;
			}

			/**
			 * Like EQ_INNER _ fieldname as described above, but may be applied
			 * to a field of type Int, Float, or Time.
			 */

			if (paramName.startsWith("GT_INNER_") || paramName.startsWith("GE_INNER_")
					|| paramName.startsWith("LT_INNER_") || paramName.startsWith("LE_INNER_")) {

				if (ext == null)
					return false;
				String type = paramName.substring(9, paramName.length());
				BsonArray paramArray = getParamBsonArray(paramValues);

				if (paramName.startsWith("GT_")) {
					if (isCompExtensionFilterPassed(type, "GT", paramArray, ext) == true)
						return true;
					else
						return false;
				}
				if (paramName.startsWith("GE_")) {
					if (isCompExtensionFilterPassed(type, "GE", paramArray, ext) == true)
						return true;
					else
						return false;
				}
				if (paramName.startsWith("LT_")) {
					if (isCompExtensionFilterPassed(type, "LT", paramArray, ext) == true)
						return true;
					else
						return false;
				}
				if (paramName.startsWith("LE_")) {
					if (isCompExtensionFilterPassed(type, "LE", paramArray, ext) == true)
						return true;
					else
						return false;
				}
			}

		}

		return true;
	}

	private boolean isExtensionFilterPassed(String type, BsonArray paramArray, BsonDocument ext, boolean isTopLevel) {
		type = MongoWriterUtil.encodeMongoObjectKey(type);
		Iterator<String> keyIterator = ext.keySet().iterator();
		while (keyIterator.hasNext()) {
			String key = keyIterator.next();
			BsonValue sub = ext.get(key);
			if (isTopLevel == false) {
				if (key.equals(type)) {
					for (int i = 0; i < paramArray.size(); i++) {
						BsonValue param = paramArray.get(i);
						if (sub.getBsonType() == param.getBsonType() && sub.toString().equals(param.toString())) {
							return true;
						}
						if (param.getBsonType() == BsonType.REGULAR_EXPRESSION
								&& sub.getBsonType() == BsonType.STRING) {
							if (Pattern.matches(param.asRegularExpression().getPattern(), sub.asString().getValue()))
								return true;
						}
					}
					return false;
				}
			}
			if (sub.getBsonType() == BsonType.DOCUMENT) {
				if (isExtensionFilterPassed(type, paramArray, sub.asDocument(), false) == true) {
					return true;
				}
			}
		}
		return false;
	}

	private boolean isCompExtensionFilterPassed(String type, String comp, BsonArray paramArray, BsonDocument ext) {
		type = MongoWriterUtil.encodeMongoObjectKey(type);
		Iterator<String> keyIterator = ext.keySet().iterator();
		while (keyIterator.hasNext()) {
			String key = keyIterator.next();
			BsonValue sub = ext.get(key);
			if (key.equals(type)) {
				for (int i = 0; i < paramArray.size(); i++) {
					BsonValue param = paramArray.get(i);
					if (sub.getBsonType() == param.getBsonType()) {
						if (sub.getBsonType() == BsonType.INT32) {
							if (comp.equals("GT")) {
								if (sub.asInt32().getValue() > param.asInt32().getValue())
									return true;
							} else if (comp.equals("GE")) {
								if (sub.asInt32().getValue() >= param.asInt32().getValue())
									return true;
							} else if (comp.equals("LT")) {
								if (sub.asInt32().getValue() < param.asInt32().getValue())
									return true;
							} else if (comp.equals("LE")) {
								if (sub.asInt32().getValue() <= param.asInt32().getValue())
									return true;
							}
						} else if (sub.getBsonType() == BsonType.INT64) {
							if (comp.equals("GT")) {
								if (sub.asInt64().getValue() > param.asInt64().getValue())
									return true;
							} else if (comp.equals("GE")) {
								if (sub.asInt64().getValue() >= param.asInt64().getValue())
									return true;
							} else if (comp.equals("LT")) {
								if (sub.asInt64().getValue() < param.asInt64().getValue())
									return true;
							} else if (comp.equals("LE")) {
								if (sub.asInt64().getValue() <= param.asInt64().getValue())
									return true;
							}
						} else if (sub.getBsonType() == BsonType.DOUBLE) {
							if (comp.equals("GT")) {
								if (sub.asDouble().getValue() > param.asDouble().getValue())
									return true;
							} else if (comp.equals("GE")) {
								if (sub.asDouble().getValue() >= param.asDouble().getValue())
									return true;
							} else if (comp.equals("LT")) {
								if (sub.asDouble().getValue() < param.asDouble().getValue())
									return true;
							} else if (comp.equals("LE")) {
								if (sub.asDouble().getValue() <= param.asDouble().getValue())
									return true;
							}
						} else if (sub.getBsonType() == BsonType.DATE_TIME) {
							if (comp.equals("GT")) {
								if (sub.asDateTime().getValue() > param.asDateTime().getValue())
									return true;
							} else if (comp.equals("GE")) {
								if (sub.asDateTime().getValue() >= param.asDateTime().getValue())
									return true;
							} else if (comp.equals("LT")) {
								if (sub.asDateTime().getValue() < param.asDateTime().getValue())
									return true;
							} else if (comp.equals("LE")) {
								if (sub.asDateTime().getValue() <= param.asDateTime().getValue())
									return true;
							}
						}
					}
				}
				return false;
			}
			if (sub.getBsonType() == BsonType.DOCUMENT) {
				if (isCompExtensionFilterPassed(type, comp, paramArray, sub.asDocument()) == true) {
					return true;
				}
			}
		}
		return false;
	}

	private BsonArray makeMasterQueryObjects(PollParameters p) {

		BsonArray queryList = new BsonArray();

		/**
		 * vocabularyName : If specified, only vocabulary elements drawn from
		 * one of the specified vocabularies will be included in the results.
		 * Each element of the specified list is the formal URI name for a
		 * vocabulary; e.g., one of the URIs specified in the table at the end
		 * of Section 7.2. If omitted, all vocabularies are considered.
		 */

		if (p.getVocabularyName() != null) {

			BsonArray paramArray = getParamBsonArray(p.getVocabularyName());
			BsonDocument queryObject = getQueryObject(new String[] { "type" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}

		}

		/**
		 * EQ_name : If specified, the result will only include vocabulary
		 * elements whose names are equal to one of the specified values. If
		 * this parameter and WD_name are both omitted, vocabulary elements are
		 * included regardless of their names.
		 */
		if (p.getEQ_name() != null) {
			BsonArray paramArray = getParamBsonArray(p.getEQ_name());
			BsonDocument queryObject = getQueryObject(new String[] { "id" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}
		}

		/**
		 * WD_name : If specified, the result will only include vocabulary
		 * elements that either match one of the specified names, or are direct
		 * or indirect descendants of a vocabulary element that matches one of
		 * the specified names. The meaning of "direct or indirect descendant"
		 * is described in Section 6.5. (WD is an abbreviation for "with
		 * descendants.") If this parameter and EQ_name are both omitted,
		 * vocabulary elements are included regardless of their names.
		 */
		if (p.getWD_name() != null) {

			BsonArray paramArray = getWDParamBsonArray(p.getWD_name());
			BsonDocument queryObject = getQueryObject(new String[] { "id" }, paramArray);
			if (queryObject != null) {
				queryList.add(queryObject);
			}

		}

		/**
		 * HASATTR : If specified, the result will only include vocabulary
		 * elements that have a non-null attribute whose name matches one of the
		 * values specified in this parameter.
		 */

		if (p.getHASATTR() != null) {
			String[] attrArr = p.getHASATTR().split(",");
			for (int i = 0; i < attrArr.length; i++) {
				String attrString = attrArr[i].trim();
				BsonDocument query = getExistsQueryObject("attributes", attrString, new BsonBoolean(true));
				if (query != null)
					queryList.add(query);
			}
		}

		/**
		 * EQATTR_attrnam : This is not a single parameter, but a family of
		 * parameters. If a parameter of this form is specified, the result will
		 * only include vocabulary elements that have a non-null attribute named
		 * attrname, and where the value of that attribute matches one of the
		 * values specified in this parameter.
		 */
		if (p.getParams() != null) {
			Iterator<String> paramIter = p.getParams().keySet().iterator();
			while (paramIter.hasNext()) {
				String paramName = paramIter.next();
				String paramValues = p.getParams().get(paramName);

				if (paramName.contains("EQATTR_")) {
					String type = paramName.substring(7, paramName.length());

					BsonArray paramArray = getParamBsonArray(paramValues);
					BsonDocument queryObject = getQueryObject(
							new String[] { "attributes." + encodeMongoObjectKey(type) }, paramArray);
					if (queryObject != null) {
						queryList.add(queryObject);
					}
				}
			}
		}
		return queryList;
	}

	public void addScheduleToQuartz(SubscriptionType subscription) {
		try {
			JobDataMap map = new JobDataMap();
			map.put("jobData", SubscriptionType.asBsonDocument(subscription));

			JobDetail job = newJob(MongoSubscriptionTask.class)
					.withIdentity(subscription.getSubscriptionID(), subscription.getPollParameters().getQueryName())
					.setJobData(map).storeDurably(false).build();

			Trigger trigger = newTrigger()
					.withIdentity(subscription.getSubscriptionID(), subscription.getPollParameters().getQueryName())
					.startNow().withSchedule(cronSchedule(subscription.getSchedule())).build();

			if (MongoSubscription.sched.isStarted() != true)
				MongoSubscription.sched.start();
			MongoSubscription.sched.scheduleJob(job, trigger);
			Configuration.logger.log(Level.INFO,
					"Subscription ID: " + subscription.getSubscriptionID() + " is added to quartz scheduler. ");
		} catch (SchedulerException e) {
			Configuration.logger.log(Level.ERROR, e.toString());
		} catch (RuntimeException e) {
			Configuration.logger.log(Level.ERROR, e.toString());
		}
	}

	private boolean addScheduleToDB(SubscriptionType s, String userID, List<String> friendList) {

		MongoCollection<BsonDocument> collection = Configuration.mongoDatabase.getCollection("Subscription",
				BsonDocument.class);

		BsonDocument subscription = collection
				.find(new BsonDocument("subscriptionID", new BsonString(s.getSubscriptionID()))).first();

		if (subscription == null) {
			collection.insertOne(SubscriptionType.asBsonDocument(s));
		}

		Configuration.logger.log(Level.INFO, "Subscription ID: " + s.getSubscriptionID() + " is added to DB. ");
		return true;
	}

	private void removeScheduleFromQuartz(SubscriptionType subscription) {
		try {
			MongoSubscription.sched.unscheduleJob(
					triggerKey(subscription.getSubscriptionID(), subscription.getPollParameters().getQueryName()));
			MongoSubscription.sched.deleteJob(
					jobKey(subscription.getSubscriptionID(), subscription.getPollParameters().getQueryName()));
			Configuration.logger.log(Level.INFO,
					"Subscription ID: " + subscription.getSubscriptionID() + " is removed from scheduler");
		} catch (SchedulerException e) {
			Configuration.logger.log(Level.ERROR, e.toString());
		}
	}

}


text:
package modelservice

import modelservice.api.{BootedCoreApi, ClusterCoreApi}
import modelservice.core.prediction.PredictionActorSet
import modelservice.core.{ClusterCore, CoreActorSet, InitCore, StatSiteLogging}
import modelservice.storage.ClusteredStorageActorSet
import modelservice.web.Web

object Boot extends App
  with InitCore
  with BootedCoreApi
  with Web
  with CoreActorSet
  with PredictionActorSet
  with ClusteredStorageActorSet
  with ClusterCore
  with StatSiteLogging

redpajama_set_name: RedPajamaGithub


text:
using System;
using System.Collections;
using System.Xml;

namespace _02.ArtistExtractor
{
    internal class ArtistExtractor
    {
        internal static void Main()
        {
            XmlDocument doc = new XmlDocument();
            string filePath = "../../../../catalog.xml";
            doc.Load(filePath);
            Hashtable table = new Hashtable();
            XmlNode catalog = doc.DocumentElement;
            foreach (XmlNode album in catalog.ChildNodes)
            {
                var currentArtist = album["artist"].InnerText;
                if (table.Contains(currentArtist))
                {
                    table[currentArtist] = (int)table[currentArtist] + 1;
                }
                else
                {
                    table.Add(currentArtist, 1);
                }
            }

            foreach (DictionaryEntry artist in table)
            {
                Console.WriteLine($"{artist.Key} - {artist.Value} albums");
            }

            Console.WriteLine();
        }
    }
}


text:
layout: candidato
published: true

title: Matías Freire
categories:
- diputados
- whitelist

featured-image: ../../images/diputados/matias-freire.png

gender: m

c_twitter:

extra_info_url: http://votainteligente.cl/election/diputados-por-distrito-28/matias-freire-vallejos

---

## Ingresos

Dinero

## Financistas

Ramón Freire **$300.000**

## Gastos

- Gigantografías **$80.000**
- Impresiones **$120.000**
- Otros **$100.000**


text:
<?php

/*
 * This file is part of DDD package.
 *
 * (c) Denis Lityagin <denis.lityagin@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled with this
 * source code in the file LICENSE.
 */

namespace Dlart\DDD\Tests\Password;

use Assert\Assertion;
use Assert\AssertionFailedException;
use Dlart\DDD\Password\PlainPassword;
use PHPUnit\Framework\TestCase;

/**
 * PlainPasswordTest.
 *
 * @author Denis Lityagin <info@dlart.ru>
 */
class PlainPasswordTest extends TestCase
{
    public function testThatPlainPasswordLengthCanNotBeLessThanMinimalExpected(): void
    {
        self::expectException(AssertionFailedException::class);
        self::expectExceptionCode(Assertion::INVALID_MIN_LENGTH);

        new PlainPassword(str_repeat(' ', PlainPassword::MIN_LENGTH - 1));
    }

    public function testThatPlainPasswordLengthCanNotBeMoreThanMaximumExpected(): void
    {
        self::expectException(AssertionFailedException::class);
        self::expectExceptionCode(Assertion::INVALID_MAX_LENGTH);

        new PlainPassword(str_repeat(' ', PlainPassword::MAX_LENGTH + 1));
    }
}


text:
module.exports = {

  friendlyName: 'Minify',
  description: 'Minify html',
  extendedDescription: '',

  sync: true,

  inputs: {
    "stream": {
      "friendlyName": "stream",
      "description": "A readable stream",
      "typeclass": "*",
      "required": false,
      "name": "stream"
    }
  },

  defaultExit: 'success',

  exits: {
    error: {
      description: 'Unexpected error occurred.',
    },
    errorNotStream: {
      description: "It's not a valid stream"
    },
    success: {
      description: 'Done.'
    },
  },

  fn: function (inputs,exits) {
    if (inputs.stream && require('isstream')(inputs.stream) !== true)
      return exits.errorNotStream({error: "It's not a valid stream"});

    var MinifyStream = require('../lib/stream-transformer.js').simple()
      , minify = require('html-minifier').minify;

    try {
      var mstream = new MinifyStream(minify);
      return exits.success( inputs.stream ? inputs.stream.pipe(mstream) : mstream);
    } catch (err) { return exits.error(err); }
  },

};


text:
Examui::Application.routes.draw do

  resources :examen
  resources :listados
  resources :opcions
  resources :pregunta
  resources :asignaturas

  devise_for :users

  get 'nueva_pregunta/:id' => 'pregunta#nueva_pregunta', :as => :nueva_pregunta
  get 'nueva_opcion/:id' => 'opcions#nueva_opcion', :as => :nueva_opcion
  get 'nuevo_examen/:id' => 'examen#new', :as => :nuevo_examen
  get 'clave/:id' => 'examen#clave', :as => :clave
  get 'generate/latex/master/:id' => 'latex#latex_master', :as => :latex_master

  post 'update_clave' => 'examen#update_clave', :as => :update_clave

  root 'asignaturas#index'
end