Merge "Switch from config-parent in pom files"
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / restconf / jersey / providers / JsonToPatchBodyReader.java
index 33265ff8c4f43197253f695086328cf73448d4d1..11388dd7b1bdc022eb7c783fb8eb4332502e3f25 100644 (file)
@@ -8,8 +8,7 @@
 
 package org.opendaylight.restconf.jersey.providers;
 
-import static org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation.isPatchOperationWithValue;
-
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.gson.stream.JsonReader;
 import com.google.gson.stream.JsonToken;
@@ -17,20 +16,16 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.StringReader;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.List;
 import javax.annotation.Nonnull;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.Provider;
 import org.opendaylight.netconf.sal.restconf.impl.ControllerContext;
 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
 import org.opendaylight.netconf.sal.restconf.impl.PatchContext;
+import org.opendaylight.netconf.sal.restconf.impl.PatchEditOperation;
 import org.opendaylight.netconf.sal.restconf.impl.PatchEntity;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
@@ -52,26 +47,17 @@ import org.slf4j.LoggerFactory;
 
 @Provider
 @Consumes({Rfc8040.MediaTypes.PATCH + RestconfConstants.JSON})
-public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
-        implements MessageBodyReader<PatchContext> {
-
+public class JsonToPatchBodyReader extends AbstractToPatchBodyReader {
     private static final Logger LOG = LoggerFactory.getLogger(JsonToPatchBodyReader.class);
-    private String patchId;
 
-    @Override
-    public boolean isReadable(final Class<?> type, final Type genericType,
-                              final Annotation[] annotations, final MediaType mediaType) {
-        return true;
-    }
+    private String patchId;
 
     @SuppressWarnings("checkstyle:IllegalCatch")
     @Override
-    public PatchContext readFrom(final Class<PatchContext> type, final Type genericType,
-                                 final Annotation[] annotations, final MediaType mediaType,
-                                 final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream)
+    protected PatchContext readBody(final InstanceIdentifierContext<?> path, final InputStream entityStream)
             throws IOException, WebApplicationException {
         try {
-            return readFrom(getInstanceIdentifierContext(), entityStream);
+            return readFrom(path, entityStream);
         } catch (final Exception e) {
             throw propagateExceptionAs(e);
         }
@@ -79,10 +65,6 @@ public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
 
     private PatchContext readFrom(final InstanceIdentifierContext<?> path, final InputStream entityStream)
             throws IOException {
-        if (entityStream.available() < 1) {
-            return new PatchContext(path, null, null);
-        }
-
         final JsonReader jsonReader = new JsonReader(new InputStreamReader(entityStream));
         final List<PatchEntity> resultList = read(jsonReader, path);
         jsonReader.close();
@@ -175,7 +157,7 @@ public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
                              @Nonnull final StringModuleInstanceIdentifierCodec codec,
                              @Nonnull final List<PatchEntity> resultCollection) throws IOException {
         switch (name) {
-            case "edit" :
+            case "edit":
                 if (in.peek() == JsonToken.BEGIN_ARRAY) {
                     in.beginArray();
 
@@ -193,7 +175,7 @@ public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
                 }
 
                 break;
-            case "patch-id" :
+            case "patch-id":
                 this.patchId = in.nextString();
                 break;
             default:
@@ -213,19 +195,19 @@ public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
     private void readEditDefinition(@Nonnull final PatchEdit edit, @Nonnull final JsonReader in,
                                     @Nonnull final InstanceIdentifierContext<?> path,
                                     @Nonnull final StringModuleInstanceIdentifierCodec codec) throws IOException {
-        final StringBuilder sb = new StringBuilder();
+        String deferredValue = null;
         in.beginObject();
 
         while (in.hasNext()) {
             final String editDefinition = in.nextName();
             switch (editDefinition) {
-                case "edit-id" :
+                case "edit-id":
                     edit.setId(in.nextString());
                     break;
-                case "operation" :
-                    edit.setOperation(in.nextString());
+                case "operation":
+                    edit.setOperation(PatchEditOperation.valueOf(in.nextString().toUpperCase()));
                     break;
-                case "target" :
+                case "target":
                     // target can be specified completely in request URI
                     final String target = in.nextString();
                     if (target.equals("/")) {
@@ -239,21 +221,35 @@ public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
                     }
 
                     break;
-                case "value" :
-                    // save data defined in value node for next (later) processing, because target needs to be read
-                    // always first and there is no ordering in Json input
-                    readValueNode(sb, in);
+                case "value":
+                    Preconditions.checkArgument(edit.getData() == null && deferredValue == null,
+                            "Multiple value entries found");
+
+                    if (edit.getTargetSchemaNode() == null) {
+                        final StringBuilder sb = new StringBuilder();
+
+                        // save data defined in value node for next (later) processing, because target needs to be read
+                        // always first and there is no ordering in Json input
+                        readValueNode(sb, in);
+                        deferredValue = sb.toString();
+                    } else {
+                        // We have a target schema node, reuse this reader without buffering the value.
+                        edit.setData(readEditData(in, edit.getTargetSchemaNode(), path));
+                    }
                     break;
                 default:
+                    // FIXME: this does not look right, as it can wreck our logic
                     break;
             }
         }
 
         in.endObject();
 
-        // read saved data to normalized node when target schema is already known
-        edit.setData(
-                readEditData(new JsonReader(new StringReader(sb.toString())), edit.getTargetSchemaNode(), path));
+        if (deferredValue != null) {
+            // read saved data to normalized node when target schema is already known
+            edit.setData(readEditData(new JsonReader(new StringReader(deferredValue)), edit.getTargetSchemaNode(),
+                path));
+        }
     }
 
     /**
@@ -372,7 +368,7 @@ public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
     private static PatchEntity prepareEditOperation(@Nonnull final PatchEdit edit) {
         if (edit.getOperation() != null && edit.getTargetSchemaNode() != null
                 && checkDataPresence(edit.getOperation(), edit.getData() != null)) {
-            if (!isPatchOperationWithValue(edit.getOperation())) {
+            if (!edit.getOperation().isWithValue()) {
                 return new PatchEntity(edit.getId(), edit.getOperation(), edit.getTarget());
             }
 
@@ -397,8 +393,8 @@ public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
      * @return true if data is present when operation requires it or if there are no data when operation does not
      *     allow it, false otherwise
      */
-    private static boolean checkDataPresence(@Nonnull final String operation, final boolean hasData) {
-        return isPatchOperationWithValue(operation) == hasData;
+    private static boolean checkDataPresence(@Nonnull final PatchEditOperation operation, final boolean hasData) {
+        return operation.isWithValue() == hasData;
     }
 
     /**
@@ -406,52 +402,52 @@ public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider
      */
     private static final class PatchEdit {
         private String id;
-        private String operation;
+        private PatchEditOperation operation;
         private YangInstanceIdentifier target;
         private SchemaNode targetSchemaNode;
         private NormalizedNode<?, ?> data;
 
-        public String getId() {
+        String getId() {
             return id;
         }
 
-        public void setId(final String id) {
-            this.id = id;
+        void setId(final String id) {
+            this.id = Preconditions.checkNotNull(id);
         }
 
-        public String getOperation() {
+        PatchEditOperation getOperation() {
             return operation;
         }
 
-        public void setOperation(final String operation) {
-            this.operation = operation;
+        void setOperation(final PatchEditOperation operation) {
+            this.operation = Preconditions.checkNotNull(operation);
         }
 
-        public YangInstanceIdentifier getTarget() {
+        YangInstanceIdentifier getTarget() {
             return target;
         }
 
-        public void setTarget(final YangInstanceIdentifier target) {
-            this.target = target;
+        void setTarget(final YangInstanceIdentifier target) {
+            this.target = Preconditions.checkNotNull(target);
         }
 
-        public SchemaNode getTargetSchemaNode() {
+        SchemaNode getTargetSchemaNode() {
             return targetSchemaNode;
         }
 
-        public void setTargetSchemaNode(final SchemaNode targetSchemaNode) {
-            this.targetSchemaNode = targetSchemaNode;
+        void setTargetSchemaNode(final SchemaNode targetSchemaNode) {
+            this.targetSchemaNode = Preconditions.checkNotNull(targetSchemaNode);
         }
 
-        public NormalizedNode<?, ?> getData() {
+        NormalizedNode<?, ?> getData() {
             return data;
         }
 
-        public void setData(final NormalizedNode<?, ?> data) {
-            this.data = data;
+        void setData(final NormalizedNode<?, ?> data) {
+            this.data = Preconditions.checkNotNull(data);
         }
 
-        public void clear() {
+        void clear() {
             id = null;
             operation = null;
             target = null;