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

com.dslplatform.json.processor.StructInfo Maven / Gradle / Ivy

There is a newer version: 1.50.0
Show newest version
package com.dslplatform.json.processor;

import com.dslplatform.json.CompiledJson;
import com.dslplatform.json.Nullable;

import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import java.util.*;

public class StructInfo {
	public final TypeElement element;
	public final DeclaredType discoveredBy;
	public final String name;
	public final String binaryName;
	public final ObjectType type;
	public final ConverterInfo converter;
	public final String jsonObjectReaderPath;
	public final List matchingConstructors;
	public final ExecutableElement annotatedFactory;
	public final ExecutableElement annotatedConstructor;
	public final BuilderInfo builder;
	public final Set implementations = new HashSet();
	public final Map minifiedNames = new HashMap();
	public final AnnotationMirror annotation;
	public final CompiledJson.Behavior onUnknown;
	public final CompiledJson.TypeSignature typeSignature;
	public final CompiledJson.ObjectFormatPolicy objectFormatPolicy;
	public final TypeElement deserializeAs;
	public final String discriminator;
	public final String deserializeName;
	public final boolean isMinified;
	public final EnumSet formats;
	public final boolean isObjectFormatFirst;
	public final LinkedHashMap attributes = new LinkedHashMap();
	public final Set properties = new HashSet();
	public final List constants = new ArrayList();
	public final Element enumConstantNameSource;
	public final Stack path = new Stack();
	public final Map unknowns = new LinkedHashMap();
	public final boolean isParameterized;
	public final List typeParametersNames;
	public final Map genericSignatures;

	private ExecutableElement selectedConstructor;
	private boolean createThroughConstructor;

	public StructInfo(
			TypeElement element,
			DeclaredType discoveredBy,
			String name,
			String binaryName,
			ObjectType type,
			@Nullable String jsonObjectReaderPath,
			@Nullable List matchingConstructors,
			@Nullable ExecutableElement annotatedConstructor,
			@Nullable ExecutableElement annotatedFactory,
			@Nullable BuilderInfo builder,
			@Nullable AnnotationMirror annotation,
			@Nullable CompiledJson.Behavior onUnknown,
			@Nullable CompiledJson.TypeSignature typeSignature,
			CompiledJson.ObjectFormatPolicy objectFormatPolicy,
			@Nullable TypeElement deserializeAs,
			@Nullable String discriminator,
			@Nullable String deserializeName,
			@Nullable Element enumConstantNameSource,
			boolean isMinified,
			@Nullable CompiledJson.Format[] formats,
			Map genericSignatures) {
		this.element = element;
		this.discoveredBy = discoveredBy;
		this.name = name;
		this.binaryName = binaryName;
		this.type = type;
		this.jsonObjectReaderPath = jsonObjectReaderPath;
		this.converter = null;
		this.matchingConstructors = matchingConstructors;
		this.annotatedFactory = annotatedFactory;
		this.builder = builder;
		this.annotation = annotation;
		this.onUnknown = onUnknown;
		this.typeSignature = typeSignature;
		this.objectFormatPolicy = objectFormatPolicy;
		this.deserializeAs = deserializeAs;
		this.discriminator = discriminator != null ? discriminator : "";
		this.deserializeName = deserializeName != null ? deserializeName : "";
		this.enumConstantNameSource = enumConstantNameSource;
		this.isMinified = isMinified;
		this.formats = formats == null ? EnumSet.of(CompiledJson.Format.OBJECT) : EnumSet.copyOf(Arrays.asList(formats));
		this.isObjectFormatFirst = formats == null || formats.length == 0 || formats[0] == CompiledJson.Format.OBJECT;
		this.createThroughConstructor = annotatedFactory == null && annotatedConstructor != null;
		if (annotatedConstructor != null) this.annotatedConstructor = this.selectedConstructor = annotatedConstructor;
		else if (matchingConstructors == null) this.annotatedConstructor = null;
		else if (matchingConstructors.size() == 1) {
			this.selectedConstructor = matchingConstructors.get(0);
			this.annotatedConstructor = null;
		} else {
			ExecutableElement emptyCtor = null;
			for (ExecutableElement ee : matchingConstructors) {
				if (ee.getParameters().size() == 0) {
					emptyCtor = ee;
					break;
				}
			}
			this.selectedConstructor = emptyCtor;
			this.annotatedConstructor = null;
		}
		this.typeParametersNames = extractParametersNames(element.getTypeParameters());
		this.isParameterized = !typeParametersNames.isEmpty();
		this.genericSignatures = genericSignatures;
	}

	public StructInfo(ConverterInfo converter, DeclaredType discoveredBy, TypeElement target, String name, String binaryName) {
		this.element = target;
		this.discoveredBy = discoveredBy;
		this.name = name;
		this.binaryName = binaryName;
		this.type = ObjectType.CONVERTER;
		this.jsonObjectReaderPath = null;
		this.converter = converter;
		this.matchingConstructors = null;
		this.annotatedConstructor = null;
		this.annotatedFactory = null;
		this.builder = null;
		this.annotation = null;
		this.onUnknown = null;
		this.typeSignature = null;
		this.objectFormatPolicy = CompiledJson.ObjectFormatPolicy.DEFAULT;
		this.deserializeAs = null;
		this.discriminator = "";
		this.deserializeName = "";
		this.enumConstantNameSource = null;
		this.isMinified = false;
		this.formats = EnumSet.of(CompiledJson.Format.OBJECT);
		this.isObjectFormatFirst = true;
		this.typeParametersNames = extractParametersNames(element.getTypeParameters());
		this.isParameterized = !typeParametersNames.isEmpty();
		this.genericSignatures = Collections.emptyMap();
	}

	private List extractParametersNames(final List typeParameters) {
		if (typeParameters.isEmpty()) return Collections.emptyList();
		List names = new ArrayList(typeParameters.size());
		for (TypeParameterElement typeParameter : typeParameters) {
			names.add(typeParameter.getSimpleName().toString());
		}
		return names;
	}

	@Nullable
	public ExecutableElement selectedConstructor() {
		return selectedConstructor;
	}

	public void useConstructor(ExecutableElement ctor) {
		if (matchingConstructors == null || !matchingConstructors.contains(ctor)) {
			throw new IllegalArgumentException("Specified ctor is not a part of matchingConstructors");
		}
		selectedConstructor = ctor;
		createThroughConstructor = true;
	}

	public boolean hasKnownConversion() {
		return jsonObjectReaderPath != null || converter != null;
	}

	public boolean usesEmptyCtor() {
		return (createThroughConstructor || annotatedFactory == null) && selectedConstructor != null && selectedConstructor.getParameters().size() == 0;
	}

	public boolean usesCtorWithArguments() {
		return (createThroughConstructor || annotatedFactory == null) && selectedConstructor != null && selectedConstructor.getParameters().size() > 0;
	}

	public boolean hasEmptyCtor() {
		if (matchingConstructors == null) return false;
		for (ExecutableElement ctor : matchingConstructors) {
			if (ctor.getParameters().isEmpty()) return true;
		}
		return false;
	}

	public boolean createFromEmptyInstance() {
		return annotatedFactory != null && annotatedFactory.getParameters().size() == 0
				|| annotatedFactory == null && selectedConstructor != null && selectedConstructor.getParameters().size() == 0;
	}

	public boolean hasAnnotation() {
		return annotation != null;
	}

	public boolean hasCycles(Map structs) {
		return hasCycles(new HashSet(), structs);
	}

	private boolean hasCycles(HashSet processed, Map structs) {
		if (type == ObjectType.ENUM || type == ObjectType.CONVERTER) return false;
		processed.add(element.asType());
		for (AttributeInfo ai : attributes.values()) {
			if (ai.converter != null || ai.isJsonObject) continue;
			if (ai.isGeneric) return true;
			for (TypeMirror tm : ai.usedTypes) {
				if (processed.add(tm)) {
					StructInfo find = structs.get(tm.toString());
					if (find != null && find.hasCycles(processed, structs)) return true;
				} else return true;
			}
		}
		return false;
	}

	public static int calcHash(String name) {
		long hash = 0x811c9dc5;
		for (int i = 0; i < name.length(); i++) {
			byte b = (byte) name.charAt(i);
			hash ^= b;
			hash *= 0x1000193;
		}
		return (int) hash;
	}

	public static int calcWeakHash(String name) {
		int hash = 0;
		for (int i = 0; i < name.length(); i++) {
			byte b = (byte) name.charAt(i);
			hash += b;
		}
		return hash;
	}

	private StructInfo deserializeTarget;
	@Nullable
	public StructInfo getDeserializeTarget() { return deserializeTarget; }
	public void setDeserializeTarget(@Nullable StructInfo value) { deserializeTarget = value; }

	public String pathDescription() {
		if (annotation != null) return " since it has @CompiledJson annotation.";
		if (path.isEmpty()) return "";
		StringBuilder sb = new StringBuilder();
		for (String p : path) {
			sb.append(p).append("->");
		}
		sb.setLength(sb.length() - 2);
		sb.insert(0, " since it's referenced through path from @CompiledJson annotation: ");
		return sb.toString();
	}

	public boolean checkHashCollision() {
		boolean hasAliases = false;
		boolean hasDuplicates = false;
		Set counters = new HashSet();
		for (AttributeInfo attr : attributes.values()) {
			int hash = calcHash(attr.alias != null ? attr.alias : attr.name);
			hasDuplicates = hasDuplicates || !counters.add(hash);
			if (!attr.alternativeNames.isEmpty()) {
				hasAliases = true;
				for (String name : attr.alternativeNames) {
					int aliasHash = calcHash(name);
					if (aliasHash == hash) {
						continue;
					}
					hasDuplicates = hasDuplicates || !counters.add(aliasHash);
				}
			}
		}
		return hasAliases && hasDuplicates;
	}

	public void prepareMinifiedNames() {
		Map counters = new HashMap();
		Set processed = new HashSet();
		Set names = new HashSet();
		for (AttributeInfo p : attributes.values()) {
			if (p.alias != null) {
				minifiedNames.put(p.id, p.alias);
				processed.add(p.id);
				names.add(p.alias);
			}
		}
		for (AttributeInfo p : attributes.values()) {
			if (processed.contains(p.id)) {
				continue;
			}
			String shortName = buildShortName(p.id, names, counters);
			minifiedNames.put(p.id, shortName);
		}
	}

	public void sortAttributes() {
		boolean needsSorting = false;
		for (AttributeInfo attr : attributes.values()) {
			needsSorting = needsSorting || attr.index >= 0;
		}
		if (needsSorting) {
			final AttributeInfo[] all = attributes.values().toArray(new AttributeInfo[0]);
			Arrays.sort(all, new Comparator() {
				@Override
				public int compare(AttributeInfo a, AttributeInfo b) {
					if (b.index == -1) return -1;
					else if (a.index == -1) return 1;
					return a.index - b.index;
				}
			});
			attributes.clear();
			for (AttributeInfo attr : all) {
				attributes.put(attr.id, attr);
			}
		}
	}

	private static String buildShortName(String name, Set names, Map counters) {
		String shortName = name.substring(0, 1);
		Character first = name.charAt(0);
		if (!names.contains(shortName)) {
			names.add(shortName);
			counters.put(first, 0);
			return shortName;
		}
		Integer next = counters.get(first);
		if (next == null) {
			next = 0;
		}
		do {
			shortName = first.toString() + next;
			next++;
		} while (names.contains(shortName));
		counters.put(first, next);
		names.add(shortName);
		return shortName;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy