// Path: src/main/java/pl/jalokim/propertiestojson/path/PathMetadata.java
// @Data
// public class PathMetadata {
// 
//     private static final String NUMBER_PATTERN = "([1-9]\\d*)|0";
//     public static final String INDEXES_PATTERN = "\\s*(\\[\\s*((" + NUMBER_PATTERN + ")|\\*)\\s*]\\s*)+";
// 
//     private static final String WORD_PATTERN = "(.)*";
// 
//     private final String originalPropertyKey;
//     private PathMetadata parent;
//     private String fieldName;
//     private String originalFieldName;
//     private PathMetadata child;
//     private PropertyArrayHelper propertyArrayHelper;
//     private Object rawValue;
//     private AbstractJsonType jsonValue;
// 
//     public boolean isLeaf() {
//         return child == null;
//     }
// 
//     public boolean isRoot() {
//         return parent == null;
//     }
// 
//     public String getCurrentFullPath() {
//         return parent == null ? originalFieldName : parent.getCurrentFullPath() + NORMAL_DOT + originalFieldName;
//     }
// 
//     public PathMetadata getLeaf() {
//         PathMetadata current = this;
//         while (current.getChild() != null) {
//             current = current.getChild();
//         }
//         return current;
//     }
// 
//     public void setFieldName(String fieldName) {
//         this.fieldName = fieldName;
//         if (fieldName.matches(WORD_PATTERN + INDEXES_PATTERN)) {
//             propertyArrayHelper = new PropertyArrayHelper(fieldName);
//             this.fieldName = propertyArrayHelper.getArrayFieldName();
//         }
//     }
// 
//     public void setRawValue(Object rawValue) {
//         if (!isLeaf()) {
//             throw new NotLeafValueException("Cannot set value for not leaf: " + getCurrentFullPath());
//         }
//         this.rawValue = rawValue;
//     }
// 
//     public String getOriginalPropertyKey() {
//         return originalPropertyKey;
//     }
// 
//     public PathMetadata getRoot() {
//         PathMetadata current = this;
//         while (current.getParent() != null) {
//             current = current.getParent();
//         }
//         return current;
//     }
// 
//     public String getCurrentFullPathWithoutIndexes() {
//         String parentFullPath = isRoot() ? EMPTY_STRING : getParent().getCurrentFullPath() + NORMAL_DOT;
//         return parentFullPath + getFieldName();
//     }
// 
//     public AbstractJsonType getJsonValue() {
//         return jsonValue;
//     }
// 
//     public void setJsonValue(AbstractJsonType jsonValue) {
//         if (!isLeaf()) {
//             throw new NotLeafValueException("Cannot set value for not leaf: " + getCurrentFullPath());
//         }
//         this.jsonValue = jsonValue;
//     }
// 
//     @Override
//     public String toString() {
//         return "field='" + fieldName + '\''
//             + ", rawValue=" + rawValue
//             + ", fullPath='" + getCurrentFullPath() + '}';
//     }
// 
//     public boolean isArrayField() {
//         return propertyArrayHelper != null;
//     }
// }
// 
// Path: src/main/java/pl/jalokim/propertiestojson/util/exception/MergeObjectException.java
// public class MergeObjectException extends RuntimeException {
// 
//     private static final long serialVersionUID = 1L;
// 
//     public MergeObjectException(AbstractJsonType oldJsonElement, AbstractJsonType elementToAdd, PathMetadata currentPathMetadata) {
//         this(oldJsonElement.toStringJson(), elementToAdd.toStringJson(), currentPathMetadata);
//     }
// 
//     @VisibleForTesting
//     public MergeObjectException(String oldJsonElementValue, String elementToAddValue, PathMetadata currentPathMetadata) {
//         super(format("Cannot merge objects with different types:%n Old object: %s%n New object: %s%n problematic key: '%s'%n with value: %s",
//             oldJsonElementValue, elementToAddValue, currentPathMetadata.getOriginalPropertyKey(), currentPathMetadata.getRawValue()));
//     }
// }


import pl.jalokim.propertiestojson.path.PathMetadata;
import pl.jalokim.propertiestojson.util.exception.MergeObjectException;
package pl.jalokim.propertiestojson.object;


@SuppressWarnings("unchecked")
public interface MergableObject<T extends AbstractJsonType> {

// Path: src/main/java/pl/jalokim/propertiestojson/path/PathMetadata.java
// @Data
// public class PathMetadata {
// 
//     private static final String NUMBER_PATTERN = "([1-9]\\d*)|0";
//     public static final String INDEXES_PATTERN = "\\s*(\\[\\s*((" + NUMBER_PATTERN + ")|\\*)\\s*]\\s*)+";
// 
//     private static final String WORD_PATTERN = "(.)*";
// 
//     private final String originalPropertyKey;
//     private PathMetadata parent;
//     private String fieldName;
//     private String originalFieldName;
//     private PathMetadata child;
//     private PropertyArrayHelper propertyArrayHelper;
//     private Object rawValue;
//     private AbstractJsonType jsonValue;
// 
//     public boolean isLeaf() {
//         return child == null;
//     }
// 
//     public boolean isRoot() {
//         return parent == null;
//     }
// 
//     public String getCurrentFullPath() {
//         return parent == null ? originalFieldName : parent.getCurrentFullPath() + NORMAL_DOT + originalFieldName;
//     }
// 
//     public PathMetadata getLeaf() {
//         PathMetadata current = this;
//         while (current.getChild() != null) {
//             current = current.getChild();
//         }
//         return current;
//     }
// 
//     public void setFieldName(String fieldName) {
//         this.fieldName = fieldName;
//         if (fieldName.matches(WORD_PATTERN + INDEXES_PATTERN)) {
//             propertyArrayHelper = new PropertyArrayHelper(fieldName);
//             this.fieldName = propertyArrayHelper.getArrayFieldName();
//         }
//     }
// 
//     public void setRawValue(Object rawValue) {
//         if (!isLeaf()) {
//             throw new NotLeafValueException("Cannot set value for not leaf: " + getCurrentFullPath());
//         }
//         this.rawValue = rawValue;
//     }
// 
//     public String getOriginalPropertyKey() {
//         return originalPropertyKey;
//     }
// 
//     public PathMetadata getRoot() {
//         PathMetadata current = this;
//         while (current.getParent() != null) {
//             current = current.getParent();
//         }
//         return current;
//     }
// 
//     public String getCurrentFullPathWithoutIndexes() {
//         String parentFullPath = isRoot() ? EMPTY_STRING : getParent().getCurrentFullPath() + NORMAL_DOT;
//         return parentFullPath + getFieldName();
//     }
// 
//     public AbstractJsonType getJsonValue() {
//         return jsonValue;
//     }
// 
//     public void setJsonValue(AbstractJsonType jsonValue) {
//         if (!isLeaf()) {
//             throw new NotLeafValueException("Cannot set value for not leaf: " + getCurrentFullPath());
//         }
//         this.jsonValue = jsonValue;
//     }
// 
//     @Override
//     public String toString() {
//         return "field='" + fieldName + '\''
//             + ", rawValue=" + rawValue
//             + ", fullPath='" + getCurrentFullPath() + '}';
//     }
// 
//     public boolean isArrayField() {
//         return propertyArrayHelper != null;
//     }
// }
// 
// Path: src/main/java/pl/jalokim/propertiestojson/util/exception/MergeObjectException.java
// public class MergeObjectException extends RuntimeException {
// 
//     private static final long serialVersionUID = 1L;
// 
//     public MergeObjectException(AbstractJsonType oldJsonElement, AbstractJsonType elementToAdd, PathMetadata currentPathMetadata) {
//         this(oldJsonElement.toStringJson(), elementToAdd.toStringJson(), currentPathMetadata);
//     }
// 
//     @VisibleForTesting
//     public MergeObjectException(String oldJsonElementValue, String elementToAddValue, PathMetadata currentPathMetadata) {
//         super(format("Cannot merge objects with different types:%n Old object: %s%n New object: %s%n problematic key: '%s'%n with value: %s",
//             oldJsonElementValue, elementToAddValue, currentPathMetadata.getOriginalPropertyKey(), currentPathMetadata.getRawValue()));
//     }
// }


// Path: src/main/java/pl/jalokim/propertiestojson/object/MergableObject.java
import pl.jalokim.propertiestojson.path.PathMetadata;
import pl.jalokim.propertiestojson.util.exception.MergeObjectException;

package pl.jalokim.propertiestojson.object;


@SuppressWarnings("unchecked")
public interface MergableObject<T extends AbstractJsonType> {

    static void mergeObjectIfPossible(AbstractJsonType oldJsonElement, AbstractJsonType elementToAdd, PathMetadata currentPathMetadata) {