com.facebook.buck.json.RawParser.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.json.RawParser.java

Source

/*
 * Copyright 2014-present Facebook, 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 com.facebook.buck.json;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.stream.JsonReader;

import java.io.IOException;
import java.io.Reader;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

/**
 * A simple JSON parser that can parse a JSON map to a raw {@code Map<String, Object>} Java object.
 */
public class RawParser {
    private static Interner<String> interner = Interners.newWeakInterner();

    /** Utility class: do not instantiate. */
    private RawParser() {
    }

    /**
     * Consumes the next JSON map from the reader, parses it as a Java {@link Map} to return, and then
     * closes the reader.
     * <p>
     * <strong>Warning:</strong> This method closes the reader.
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> parseFromReader(Reader reader) throws IOException {
        JsonReader jsonReader = new JsonReader(reader);
        try {
            JsonObject json = new Gson().fromJson(reader, JsonObject.class);
            Map<String, Object> map = (Map<String, Object>) toRawTypes(json);
            return Preconditions.checkNotNull(map);
        } finally {
            jsonReader.close();
        }
    }

    /**
     * @return One of: String, Boolean, Long, Number, List<Object>, Map<String, Object>.
     */
    @Nullable
    @VisibleForTesting
    static Object toRawTypes(JsonElement json) {
        // Cases are ordered from most common to least common.
        if (json.isJsonPrimitive()) {
            JsonPrimitive primitive = json.getAsJsonPrimitive();
            if (primitive.isString()) {
                return interner.intern(primitive.getAsString());
            } else if (primitive.isBoolean()) {
                return primitive.getAsBoolean();
            } else if (primitive.isNumber()) {
                Number number = primitive.getAsNumber();
                // Number is likely an instance of class com.google.gson.internal.LazilyParsedNumber.
                if (number.longValue() == number.doubleValue()) {
                    return number.longValue();
                } else {
                    return number;
                }
            } else {
                throw new IllegalStateException("Unknown primitive type: " + primitive);
            }
        } else if (json.isJsonArray()) {
            JsonArray array = json.getAsJsonArray();
            List<Object> out = Lists.newArrayListWithCapacity(array.size());
            for (JsonElement item : array) {
                out.add(toRawTypes(item));
            }
            return out;
        } else if (json.isJsonObject()) {
            Map<String, Object> out = new LinkedHashMap<>(json.getAsJsonObject().entrySet().size());
            for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
                // On a large project, without invoking intern(), we have seen `buck targets` OOM. When this
                // happened, according to the .hprof file generated using -XX:+HeapDumpOnOutOfMemoryError,
                // 39.6% of the memory was spent on char[] objects while 14.5% was spent on Strings.
                // (Another 10.5% was spent on java.util.HashMap$Entry.) Introducing intern() stopped the
                // OOM from happening.
                out.put(interner.intern(entry.getKey()), toRawTypes(entry.getValue()));
            }
            return out;
        } else if (json.isJsonNull()) {
            return null;
        } else {
            throw new IllegalStateException("Unknown type: " + json);
        }
    }

}