private Object deserialize()

in gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONTypeDeserializer.java [109:233]


    private Object deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException {
        final TokenBuffer buf = new TokenBuffer(jsonParser.getCodec(), false);
        final TokenBuffer localCopy = new TokenBuffer(jsonParser.getCodec(), false);

        // Detect type
        try {
            // The Type pattern is START_OBJECT -> TEXT_FIELD(propertyName) && TEXT_FIELD(valueProp).
            if (jsonParser.getCurrentToken() == JsonToken.START_OBJECT) {
                buf.writeStartObject();
                String typeName = null;
                boolean valueDetected = false;
                boolean valueDetectedFirst = false;

                for (int i = 0; i < 2; i++) {
                    String nextFieldName = jsonParser.nextFieldName();
                    if (nextFieldName == null) {
                        // empty map or less than 2 fields, go out.
                        break;
                    }
                    if (!nextFieldName.equals(this.propertyName) && !nextFieldName.equals(this.valuePropertyName)) {
                        // no type, go out.
                        break;
                    }

                    if (nextFieldName.equals(this.propertyName)) {
                        // detected "@type" field.
                        typeName = jsonParser.nextTextValue();
                        // keeping the spare buffer up to date in case it's a false detection (only the "@type" property)
                        buf.writeStringField(this.propertyName, typeName);
                        continue;
                    }
                    if (nextFieldName.equals(this.valuePropertyName)) {
                        // detected "@value" field.
                        jsonParser.nextValue();

                        if (typeName == null) {
                            // keeping the spare buffer up to date in case it's a false detection (only the "@value" property)
                            // the problem is that the fields "@value" and "@type" could be in any order
                            buf.writeFieldName(this.valuePropertyName);
                            valueDetectedFirst = true;
                            localCopy.copyCurrentStructure(jsonParser);
                        }
                        valueDetected = true;
                    }
                }

                if (typeName != null && valueDetected) {
                    // Type has been detected pattern detected.
                    final JavaType typeFromId = idRes.typeFromId(deserializationContext, typeName);

                    // the type needs to match what we are deserializing with except for TraversalStrategy which gets
                    // coerced to a TraversalStrategyProxy
                    if (!baseType.isJavaLangObject() && !baseType.equals(typeFromId) &&
                            !(baseType.getRawClass() == TraversalStrategyProxy.class && TraversalStrategy.class.isAssignableFrom(typeFromId.getRawClass()))) {
                        throw new InstantiationException(
                                String.format("Cannot deserialize the value with the detected type contained in the JSON ('%s') " +
                                        "to the type specified in parameter to the object mapper (%s). " +
                                        "Those types are incompatible.", typeName, baseType.getRawClass().toString())
                        );
                    }

                    final JsonDeserializer jsonDeserializer = deserializationContext.findContextualValueDeserializer(typeFromId, null);

                    JsonParser tokenParser;

                    if (valueDetectedFirst) {
                        tokenParser = localCopy.asParser();
                        tokenParser.nextToken();
                    } else {
                        tokenParser = jsonParser;
                    }

                    final Object value = jsonDeserializer.deserialize(tokenParser, deserializationContext);

                    final JsonToken t = jsonParser.nextToken();
                    if (t == JsonToken.END_OBJECT) {
                        // we're good to go
                        return value;
                    } else {
                        // detected the type pattern entirely but the Map contained other properties
                        // For now we error out because we assume that pattern is *only* reserved to
                        // typed values.
                        throw deserializationContext.mappingException("Detected the type pattern in the JSON payload " +
                                "but the map containing the types and values contains other fields. This is not " +
                                "allowed by the deserializer.");
                    }
                }
            }
        } catch (Exception e) {
            throw deserializationContext.mappingException("Could not deserialize the JSON value as required. Nested exception: " + e);
        }

        // Type pattern wasn't detected, however,
        // while searching for the type pattern, we may have moved the cursor of the original JsonParser in param.
        // To compensate, we have filled consistently a TokenBuffer that should contain the equivalent of
        // what we skipped while searching for the pattern.
        // This has a huge positive impact on performances, since JsonParser does not have a 'rewind()',
        // the only other solution would have been to copy the whole original JsonParser. Which we avoid here and use
        // an efficient structure made of TokenBuffer + JsonParserSequence/Concat.
        // Concatenate buf + localCopy + end of original content(jsonParser).
        final JsonParser[] concatenatedArray = {buf.asParser(), localCopy.asParser(), jsonParser};
        final JsonParser parserToUse = new JsonParserConcat(concatenatedArray);
        parserToUse.nextToken();

        // If a type has been specified in parameter, use it to find a deserializer and deserialize:
        if (!baseType.isJavaLangObject()) {
            final JsonDeserializer jsonDeserializer = deserializationContext.findContextualValueDeserializer(baseType, null);
            return jsonDeserializer.deserialize(parserToUse, deserializationContext);
        }
        // Otherwise, detect the current structure:
        else {
            if (parserToUse.isExpectedStartArrayToken()) {
                return deserializationContext.findContextualValueDeserializer(arrayJavaType, null).deserialize(parserToUse, deserializationContext);
            } else if (parserToUse.isExpectedStartObjectToken()) {
                return deserializationContext.findContextualValueDeserializer(mapJavaType, null).deserialize(parserToUse, deserializationContext);
            } else {
                // There's "java.lang.Object" in param, there's no type detected in the payload, the payload isn't a JSON Map or JSON List
                // then consider it a simple type, even though we shouldn't be here if it was a simple type.
                // TODO : maybe throw an error instead?
                // throw deserializationContext.mappingException("Roger, we have a problem deserializing");
                final JsonDeserializer jsonDeserializer = deserializationContext.findContextualValueDeserializer(baseType, null);
                return jsonDeserializer.deserialize(parserToUse, deserializationContext);
            }
        }
    }