@@ -239,33 +239,61 @@ private static boolean addProperty(ObjectNode whereToAdd, String key, JsonNode v
239239 * @param id the "id" of the property.
240240 */
241241 public void addIdProperty (String name , String id ) {
242- JsonNode jsonNode = addToIdProperty (name , id , this .properties .get (name ));
243- if (jsonNode != null ) {
244- this .linkedTo .add (id );
245- this .properties .set (name , jsonNode );
246- this .notifyObservers ();
247- }
242+ if (id == null || id .isBlank ()) { return ; }
243+ mergeIdIntoValue (id , this .properties .get (name ))
244+ .ifPresent (newValue -> {
245+ this .properties .set (name , newValue );
246+ });
247+ this .linkedTo .add (id );
248+ this .notifyObservers ();
248249 }
249250
250- private static JsonNode addToIdProperty (String name , String id , JsonNode property ) {
251- ObjectMapper objectMapper = MyObjectMapper .getMapper ();
252- if (name != null && id != null ) {
253- if (property == null ) {
254- return objectMapper .createObjectNode ().put ("@id" , id );
255- } else {
256- if (property .isArray ()) {
257- ArrayNode ns = (ArrayNode ) property ;
258- ns .add (objectMapper .createObjectNode ().put ("@id" , id ));
259- return ns ;
260- } else {
261- ArrayNode newNodes = objectMapper .createArrayNode ();
262- newNodes .add (property );
263- newNodes .add (objectMapper .createObjectNode ().put ("@id" , id ));
264- return newNodes ;
265- }
266- }
251+ /**
252+ * Merges the given id into the current value,
253+ * using this representation: {"@id" : "id"}.
254+ * <p>
255+ * The current value can be null without errors.
256+ * Only the id will be considered in this case.
257+ * <p>
258+ * If the id is null-ish, it will not be added, similar to a null-ish value.
259+ * If the id is already present, nothing will be done.
260+ * If it is not an array and the id is not present, an array will be applied.
261+ *
262+ * @param id the id to add.
263+ * @param currentValue the current value of the property.
264+ * @return The updated value of the property.
265+ * Empty if value does not change!
266+ */
267+ protected static Optional <JsonNode > mergeIdIntoValue (String id , JsonNode currentValue ) {
268+ if (id == null || id .isBlank ()) { return Optional .empty (); }
269+
270+ ObjectMapper jsonBuilder = MyObjectMapper .getMapper ();
271+ ObjectNode newIdObject = jsonBuilder .createObjectNode ().put ("@id" , id );
272+ if (currentValue == null || currentValue .isNull () || currentValue .isMissingNode ()) {
273+ return Optional .ofNullable (newIdObject );
274+ }
275+
276+ boolean isIdAlready = currentValue .asText ().equals (id );
277+ boolean isIdObjectAlready = currentValue .path ("@id" ).asText ().equals (id );
278+ boolean isArrayWithIdPresent = currentValue .valueStream ()
279+ .anyMatch (node -> node
280+ .path ("@id" )
281+ .asText ()
282+ .equals (id ));
283+ if (isIdAlready || isIdObjectAlready || isArrayWithIdPresent ) {
284+ return Optional .empty ();
285+ }
286+
287+ if (currentValue .isArray () && currentValue instanceof ArrayNode currentValueAsArray ) {
288+ currentValueAsArray .add (newIdObject );
289+ return Optional .of (currentValueAsArray );
290+ } else {
291+ // property is not an array, so we make it an array
292+ ArrayNode newNodes = jsonBuilder .createArrayNode ();
293+ newNodes .add (currentValue );
294+ newNodes .add (newIdObject );
295+ return Optional .of (newNodes );
267296 }
268- return null ;
269297 }
270298
271299 /**
@@ -369,6 +397,11 @@ protected String getId() {
369397 /**
370398 * Setting the id property of the entity, if the given value is not
371399 * null. If the id is not encoded, the encoding will be done.
400+ * <p>
401+ * <b>NOTE: IDs are not just names!</b> The ID may have effects
402+ * on parts of your crate! For example: If the entity represents a
403+ * file which will be copied into the crate, writers must use the
404+ * ID as filename.
372405 *
373406 * @param id the String representing the id.
374407 * @return the generic builder.
@@ -486,11 +519,11 @@ public T addProperty(String key, boolean value) {
486519 * @return the generic builder
487520 */
488521 public T addIdProperty (String name , String id ) {
489- JsonNode jsonNode = AbstractEntity .addToIdProperty ( name , id , this .properties .get (name ));
490- if ( jsonNode != null ) {
491- this .properties .set (name , jsonNode );
492- this .relatedItems .add (id );
493- }
522+ AbstractEntity .mergeIdIntoValue ( id , this .properties .get (name ))
523+ . ifPresent ( newValue -> {
524+ this .properties .set (name , newValue );
525+ this .relatedItems .add (id );
526+ });
494527 return self ();
495528 }
496529
@@ -526,20 +559,62 @@ public T addIdFromCollectionOfEntities(String name, Collection<AbstractEntity> e
526559 }
527560
528561 /**
529- * This sets everything from a json object to the property. Can be
530- * useful when the entity is already available somewhere.
562+ * Deprecated. Equivalent to {@link #setAllIfValid(ObjectNode)}.
531563 *
532564 * @param properties the Json representing all the properties.
533- * @return the generic builder.
565+ * @return the generic builder, either including all given properties
566+ * * or unchanged.
567+ *
568+ * @deprecated To enforce the user know what this method does,
569+ * we want the user to use one of the more explicitly named
570+ * methods {@link #setAllIfValid(ObjectNode)} or
571+ * {@link #setAllIfValid(ObjectNode)}.
572+ * @see #setAllIfValid(ObjectNode)
534573 */
574+ @ Deprecated (since = "2.1.0" , forRemoval = true )
535575 public T setAll (ObjectNode properties ) {
576+ return setAllIfValid (properties );
577+ }
578+
579+ /**
580+ * This sets everything from a json object to the property,
581+ * <b>if the result is valid</b>. Otherwise, it will do <b>nothing</b>.
582+ * <p>
583+ * Valid means here that the json object needs to be flat as specified
584+ * in the RO-Crate specification. In principle, this means that
585+ * primitives and objects referencing an ID are allowed,
586+ * as well as arrays of these.
587+ *
588+ * @param properties the Json representing all the properties.
589+ * @return the generic builder, either including all given properties
590+ * or unchanged.
591+ */
592+ public T setAllIfValid (ObjectNode properties ) {
536593 if (AbstractEntity .entityValidation .entityValidation (properties )) {
537594 this .properties = properties ;
538595 this .relatedItems .addAll (JsonUtilFunctions .getIdPropertiesFromJsonNode (properties ));
539596 }
540597 return self ();
541598 }
542599
600+ /**
601+ * This sets everything from a json object to the property. Can be
602+ * useful when the entity is already available somewhere.
603+ * <p>
604+ * Errors on validation are printed, but everything will be added.
605+ * For more about validation, see {@link #setAllIfValid(ObjectNode)}.
606+ *
607+ * @param properties the Json representing all the properties.
608+ * @return the generic builder with all properties added.
609+ */
610+ public T setAllUnsafe (ObjectNode properties ) {
611+ // This will currently only print errors.
612+ AbstractEntity .entityValidation .entityValidation (properties );
613+ this .properties = properties ;
614+ this .relatedItems .addAll (JsonUtilFunctions .getIdPropertiesFromJsonNode (properties ));
615+ return self ();
616+ }
617+
543618 public abstract T self ();
544619
545620 public abstract AbstractEntity build ();
0 commit comments