All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aQute.json.codec.ReferenceHandler Maven / Gradle / Ivy

package aQute.json.codec;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import aQute.json.codec.ObjectVisitor.Visitor;
import aQute.lib.converter.Converter;

public class ReferenceHandler {
	final static Logger			log			= LoggerFactory
			.getLogger(ReferenceHandler.class);
	final Map	roots		= new HashMap();
	final Map	paths		= new HashMap<>();
	final Map	resolved	= new HashMap<>();
	final static Pattern		REF_P		= Pattern
			.compile("(?[^#]*)#/(?.*)");

	public ReferenceHandler(Object root, Map roots) {
		if (roots != null)
			this.roots.putAll(roots);
		this.roots.put(null, root);
	}

	public Object locate(String path) {
		Matcher m = REF_P.matcher(path);
		if (!m.matches())
			throw new IllegalArgumentException("Invalid path " + path);

		Object replacement = paths.get(path);
		if (replacement != null)
			return replacement;

		String root = m.group("root");
		String sub = m.group("sub");

		if (root.isEmpty())
			replacement = get(null, sub);
		else
			replacement = get(root, sub);

		if (replacement == null)
			throw new RuntimeException("Ref to " + path + " not found");

		paths.put(path, replacement);

		//
		// Handle references to references ...
		//

		String recursive = getPath(replacement);
		if (recursive != null)
			return locate(recursive);

		if (replacement instanceof Map) {
			@SuppressWarnings({ "unchecked", "rawtypes" })
			Map map = (Map) replacement;
			if (map.size() == 1 && map.containsKey("$ref"))
				return locate(map.get("$ref"));
		}
		return replacement;
	}

	public void resolve(Object resolve) {
		ObjectVisitor.visit(new Visitor() {

			@Override
			public Object visit(Object object) {
				if (object == resolve)
					return object;

				String path = getPath(object);
				if (path == null)
					return object;

				//
				// object == DTO since it has a $ref field
				//

				if (resolved.containsKey(path))
					return resolved.get(path);

				//
				// We use the first object we find
				// as the anchor. Other references to the
				// same path use this first object
				//

				resolved.put(path, object);

				Object replacement = locate(path);
				if (object == replacement)
					return object;

				if (object.getClass() != replacement.getClass())
					replacement = convert(object.getClass(), replacement);

				copy(replacement, object);
				set(object, "$ref", path);
				resolve(object);
				return object;
			}
		}, resolve);
	}

	private  T convert(Class type, Object replacement) {
		try {
			return Converter.cnv(type, replacement);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	void copy(Object from, Object to) {
		for (Field f : ObjectVisitor.getFields(from)) {
			try {
				f.set(to, f.get(from));
			} catch (Exception e) {
				log.error("failed to copy {}", f);
			}
		}
	}

	@SuppressWarnings({ "unchecked" })
	private boolean set(Object target, String field, Object path) {
		try {

			Field f = getField(target, field);
			if (f != null) {
				f.set(target, Converter.cnv(f.getGenericType(), path));
				return true;
			}
		} catch (Exception e) {
			log.warn("set ref {}", e);
			try {
				Field f = getField(target, "__extra");
				if (f != null) {
					Map map = (Map) f
							.get(target);
					map.put("$ref", path);
					return true;
				}
			} catch (Exception ee) {
				log.warn("set ref {}", ee);
				// ignore
			}
		}
		return false;
	}

	private Object get(String root, String sub) {
		Object r = roots.get(root);

		return get(r, sub.split("/"), 0);
	}

	@SuppressWarnings("rawtypes")
	private Object get(Object r, String[] split, int i) {
		if (r == null || split.length <= i)
			return r;

		String segment = split[i];
		if (r instanceof Map) {
			Map map = (Map) r;
			return get(map.get(segment), split, i + 1);
		}
		if (r instanceof List) {
			List list = (List) r;
			int index = getInt(segment);
			return get(list.get(index), split, i + 1);
		}
		if (r instanceof Collection) {
			Collection coll = (Collection) r;
			Object[] array = coll.toArray();
			int index = getInt(segment);
			return get(array[index], split, i + 1);
		}

		if (r.getClass().isArray()) {
			int index = getInt(segment);
			return Array.get(r, index);
		}

		Field field = getField(r, segment);
		if (field == null)
			field = getField(r, segment + "$");
		if (field == null)
			return null;

		return get(get(r, field), split, i + 1);
	}

	protected int getInt(String segment) {
		return Integer.parseInt(segment);
	}

	protected Field getField(Object r, String segment) {
		try {
			if (r == null) {
				System.out.println("??");
			}
			return r.getClass().getField(segment);
		} catch (NoSuchFieldException | SecurityException e) {
			// ignore
		}
		return null;
	}

	private String getPath(Object object) {
		Field f = getField(object, "$ref");
		if (f == null || f.getType() != String.class)
			return null;

		return (String) get(object, f);
	}

	protected Object get(Object object, Field f) {
		try {
			return f.get(object);
		} catch (Exception e) {
			// ignore
		}
		return null;
	}

	public static void resolve(Object root, Map roots) {
		ReferenceHandler rh = new ReferenceHandler(root, roots);
		rh.resolve(root);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy