/** * Copyright (c) 2016 MapR, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ojai.util; import java.math.BigDecimal; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.ojai.Document; import org.ojai.DocumentBuilder; import org.ojai.DocumentReader; import org.ojai.FieldPath; import org.ojai.Value; import org.ojai.annotation.API; import org.ojai.annotation.API.NonNullable; import org.ojai.annotation.API.Nullable; import org.ojai.exceptions.ReadOnlyObjectException; import org.ojai.exceptions.TypeException; import org.ojai.json.impl.JsonUtils; import org.ojai.types.ODate; import org.ojai.types.OInterval; import org.ojai.types.OTime; import org.ojai.types.OTimestamp; import org.ojai.util.impl.ReadOnlyDocument; import com.google.common.collect.Maps; /** * This class contains utility methods for {@link Document} interface. */ @API.Public public class Documents { /** * Compares two documents for equality. * @param d1 the first document to compare * @param d2 the second document to compare * @return {@code true} if both the documents are equal, * {@code false} otherwise. */ public static boolean equals(@Nullable Document d1, @Nullable Document d2) { if (d1 == d2) { return true; // both are null or same reference } else if (d1 == null || d2 == null || d1.size() != d2.size()) { return false; } else { Map<String, Value> keyValues = Maps.newTreeMap(); Iterator<Entry<String, Value>> i = d2.iterator(); while (i.hasNext()) { Entry<String, Value> e = i.next(); keyValues.put(e.getKey(), e.getValue()); } Iterator<Entry<String, Value>> j = d1.iterator(); while (j.hasNext()) { Entry<String, Value> e = j.next(); String k = e.getKey(); Value v = keyValues.get(k); if (v == null || !e.getValue().equals(v)) { return false; } } } return true; } /** * Creates and return a read-only view of the specified OJAI document.<p/> * Any attempt to modify the returned document will result in {@link ReadOnlyObjectException} being thrown. * * @param src the source {@link Document} * @return a read only view of the specified Document */ public static Document readOnly(final Document src) { return new ReadOnlyDocument(src); } /** * This method can be used to build a {@link Document} (via {@link DocumentBuilder}) from * a {@link DocumentReader} instance. * * @param reader instance of DocumentReader to read the fields from * @param builder instance of DocumentBuilder to write the field to */ public static void writeReaderToBuilder(@NonNullable DocumentReader reader, @NonNullable DocumentBuilder builder) { JsonUtils.addToMap(reader, builder); } /** * Returns the value at the specified fieldPath as a {@link ByteBuffer} object or * the specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * <code>BINARY</code> type */ public static ByteBuffer getBinary(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable ByteBuffer defaultValue) { ByteBuffer docValue = document.getBinary(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link ByteBuffer} object or * the specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * <code>BINARY</code> type */ public static ByteBuffer getBinary(@NonNullable Document document, @NonNullable String fieldPath, @Nullable ByteBuffer defaultValue) { return getBinary(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link boolean} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * <code>BOOLEAN</code> type */ public static boolean getBoolean(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable boolean defaultValue) { Boolean docValue = document.getBooleanObj(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link boolean} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * <code>BOOLEAN</code> type */ public static boolean getBoolean(@NonNullable Document document, @NonNullable String fieldPath, @Nullable boolean defaultValue) { return getBoolean(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link byte} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static byte getByte(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable byte defaultValue) { Byte docValue = document.getByteObj(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link byte} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static byte getByte(@NonNullable Document document, @NonNullable String fieldPath, byte defaultValue) { return getByte(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link ODate} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>DATE</code> type */ public static ODate getDate(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable ODate defaultValue) { ODate docValue = document.getDate(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link ODate} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>DATE</code> type */ public static ODate getDate(@NonNullable Document document, @NonNullable String fieldPath, @Nullable ODate defaultValue) { return getDate(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link BigDecimal} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static BigDecimal getDecimal(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable BigDecimal defaultValue) { BigDecimal docValue = document.getDecimal(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link BigDecimal} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static BigDecimal getDecimal(@NonNullable Document document, @NonNullable String fieldPath, @Nullable BigDecimal defaultValue) { return getDecimal(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link double} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static double getDouble(@NonNullable Document document, @NonNullable FieldPath fieldPath, double defaultValue) { Double docValue = document.getDoubleObj(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link double} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static double getDouble(@NonNullable Document document, @NonNullable String fieldPath, @Nullable double defaultValue) { return getDouble(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link float} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static float getFloat(@NonNullable Document document, @NonNullable FieldPath fieldPath, float defaultValue) { Float docValue = document.getFloatObj(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link float} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static float getFloat(@NonNullable Document document, @NonNullable String fieldPath, @Nullable float defaultValue) { return getFloat(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link int} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static int getInt(@NonNullable Document document, @NonNullable FieldPath fieldPath, int defaultValue) { Integer docValue = document.getIntObj(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link int} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static int getInt(@NonNullable Document document, @NonNullable String fieldPath, @Nullable int defaultValue) { return getInt(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link OInterval} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>INTERVAL</code> type */ public static OInterval getInterval(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable OInterval defaultValue) { OInterval docValue = document.getInterval(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link OInterval} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>INTERVAL</code> type */ public static OInterval getInterval(@NonNullable Document document, @NonNullable String fieldPath, @Nullable OInterval defaultValue) { return getInterval(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link List} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>ARRAY</code> type */ public static List<Object> getList(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable List<Object> defaultValue) { List<Object> docValue = document.getList(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link List} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>ARRAY</code> type */ public static List<Object> getList(@NonNullable Document document, @NonNullable String fieldPath, @Nullable List<Object> defaultValue) { return getList(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link long} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static long getLong(@NonNullable Document document, @NonNullable FieldPath fieldPath, long defaultValue) { Long docValue = document.getLongObj(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link long} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static long getLong(@NonNullable Document document, @NonNullable String fieldPath, long defaultValue) { return getLong(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link Map} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>MAP</code> type */ public static Map<String, Object> getMap(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable Map<String, Object> defaultValue) { Map<String, Object> docValue = document.getMap(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link Map} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>MAP</code> type */ public static Map<String, Object> getMap(@NonNullable Document document, @NonNullable String fieldPath, @Nullable Map<String, Object> defaultValue) { return getMap(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link short} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static short getShort(@NonNullable Document document, @NonNullable FieldPath fieldPath, short defaultValue) { Short docValue = document.getShortObj(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link short} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not one of * the numeric types */ public static short getShort(@NonNullable Document document, @NonNullable String fieldPath, short defaultValue) { return getShort(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link String} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>STRING</code> type */ public static String getString(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable String defaultValue) { String docValue = document.getString(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link String} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>STRING</code> type */ public static String getString(@NonNullable Document document, @NonNullable String fieldPath, @Nullable String defaultValue) { return getString(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link OTime} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>TIME</code> type */ public static OTime getTime(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable OTime defaultValue) { OTime docValue = document.getTime(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link OTime} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>TIME</code> type */ public static OTime getTime(@NonNullable Document document, @NonNullable String fieldPath, @Nullable OTime defaultValue) { return getTime(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link OTimestamp} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>TIMESTAMP</code> type */ public static OTimestamp getTimestamp(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable OTimestamp defaultValue) { OTimestamp docValue = document.getTimestamp(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link OTimestamp} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. * * @throws TypeException if the value at the fieldPath is not of * the <code>TIMESTAMP</code> type */ public static OTimestamp getTimestamp(@NonNullable Document document, @NonNullable String fieldPath, @Nullable OTimestamp defaultValue) { return getTimestamp(document, FieldPath.parseFrom(fieldPath), defaultValue); } /** * Returns the value at the specified fieldPath as a {@link Value} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. */ public static Value getValue(@NonNullable Document document, @NonNullable FieldPath fieldPath, @Nullable Value defaultValue) { Value docValue = document.getValue(fieldPath); return docValue != null ? docValue : defaultValue; } /** * Returns the value at the specified fieldPath as a {@link Value} or the * specified {@code defaultValue} if the specified {@code FieldPath} does not * exist in the document. */ public static Value getValue(@NonNullable Document document, @NonNullable String fieldPath, @Nullable Value defaultValue) { return getValue(document, FieldPath.parseFrom(fieldPath), defaultValue); } }