entry : oldSchemas.entrySet()) {
+ entry.setValue(visitor.getResolved(entry.getValue()));
+ }
+ resolvingVisitor = visitor;
+ } finally {
+ Schema.setNameValidator(saved);
+ }
+ }
+ }
+
+ /**
+ * Resolve unresolved references in a schema that was parsed for this
+ * context using the types known to this context. Note: this method will
+ * ensure all known schemas are resolved, or throw, and thus requires the
+ * context to be committed.
+ *
+ * @param schema the schema resolve
+ * @return the fully resolved schema
+ * @throws AvroTypeException if a schema reference cannot be resolved
+ */
+ public Schema resolve(Schema schema) {
+ ensureSchemasAreResolved();
+
+ // As all (named) schemas are resolved now, we know:
+ // — All named types are either in oldSchemas or unknown.
+ // — All unnamed types can be visited&resolved without validation.
+
+ if (NAMED_SCHEMA_TYPES.contains(schema.getType()) && schema.getFullName() != null) {
+ return requireNonNull(oldSchemas.get(schema.getFullName()), () -> "Unknown schema: " + schema.getFullName());
+ } else {
+ // Unnamed or anonymous schema
+ // (protocol message request parameters are anonymous records)
+ Schemas.visit(schema, resolvingVisitor); // This field is set, as ensureSchemasAreResolved(); was called.
+ return resolvingVisitor.getResolved(schema);
+ }
+ }
+
+ /**
+ * Return all known types by their fullname. Warning: this returns all types,
+ * even uncommitted ones, including unresolved references!
*
* @return a map of all types by their name
*/
diff --git a/lang/java/avro/src/main/java/org/apache/avro/Protocol.java b/lang/java/avro/src/main/java/org/apache/avro/Protocol.java
index e837e6008a4..905f2778c6b 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/Protocol.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/Protocol.java
@@ -17,12 +17,6 @@
*/
package org.apache.avro;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.JsonNode;
-import org.apache.avro.Schema.Field;
-import org.apache.avro.Schema.Field.Order;
-
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
@@ -42,6 +36,12 @@
import java.util.Objects;
import java.util.Set;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.apache.avro.Schema.Field;
+import org.apache.avro.Schema.Field.Order;
+
/**
* A set of messages forming an application protocol.
*
@@ -79,9 +79,9 @@ public class Protocol extends JsonProperties {
/** A protocol message. */
public class Message extends JsonProperties {
- private String name;
- private String doc;
- private Schema request;
+ private final String name;
+ private final String doc;
+ private final Schema request;
/** Construct a message. */
private Message(String name, String doc, JsonProperties propMap, Schema request) {
@@ -132,7 +132,7 @@ public String toString() {
try {
StringWriter writer = new StringWriter();
JsonGenerator gen = Schema.FACTORY.createGenerator(writer);
- toJson(gen);
+ toJson(new HashSet<>(), gen);
gen.flush();
return writer.toString();
} catch (IOException e) {
@@ -140,19 +140,19 @@ public String toString() {
}
}
- void toJson(JsonGenerator gen) throws IOException {
+ void toJson(Set knownNames, JsonGenerator gen) throws IOException {
gen.writeStartObject();
if (doc != null)
gen.writeStringField("doc", doc);
writeProps(gen); // write out properties
gen.writeFieldName("request");
- request.fieldsToJson(types, gen);
+ request.fieldsToJson(knownNames, namespace, gen);
- toJson1(gen);
+ toJson1(knownNames, gen);
gen.writeEndObject();
}
- void toJson1(JsonGenerator gen) throws IOException {
+ void toJson1(Set knownNames, JsonGenerator gen) throws IOException {
gen.writeStringField("response", "null");
gen.writeBooleanField("one-way", true);
}
@@ -177,9 +177,9 @@ public String getDoc() {
}
}
- private class TwoWayMessage extends Message {
- private Schema response;
- private Schema errors;
+ private final class TwoWayMessage extends Message {
+ private final Schema response;
+ private final Schema errors;
/** Construct a message. */
private TwoWayMessage(String name, String doc, Map propMap, Schema request, Schema response,
@@ -227,15 +227,15 @@ public int hashCode() {
}
@Override
- void toJson1(JsonGenerator gen) throws IOException {
+ void toJson1(Set knownNames, JsonGenerator gen) throws IOException {
gen.writeFieldName("response");
- response.toJson(types, gen);
+ response.toJson(knownNames, namespace, gen);
List errs = errors.getTypes(); // elide system error
if (errs.size() > 1) {
Schema union = Schema.createUnion(errs.subList(1, errs.size()));
gen.writeFieldName("errors");
- union.toJson(types, gen);
+ union.toJson(knownNames, namespace, gen);
}
}
@@ -245,7 +245,7 @@ void toJson1(JsonGenerator gen) throws IOException {
private String namespace;
private String doc;
- private Schema.Names types = new Schema.Names();
+ private ParseContext context = new ParseContext();
private final Map messages = new LinkedHashMap<>();
private byte[] md5;
@@ -267,6 +267,7 @@ private Protocol() {
* {@code doc}, and {@code namespace} as {code p} has. It also copies all the
* {@code props}.
*/
+ @SuppressWarnings("CopyConstructorMissesField")
public Protocol(Protocol p) {
this(p.getName(), p.getDoc(), p.getNamespace());
putAll(p);
@@ -294,7 +295,6 @@ private void setName(String name, String namespace) {
if (this.namespace != null && this.namespace.isEmpty()) {
this.namespace = null;
}
- types.space(this.namespace);
}
/** The name of this protocol. */
@@ -314,19 +314,30 @@ public String getDoc() {
/** The types of this protocol. */
public Collection getTypes() {
- return types.values();
+ return context.resolveAllSchemas();
+ }
+
+ /** @deprecated can return invalid schemata: do NOT use! */
+ @Deprecated
+ public Collection getUnresolvedTypes() {
+ return context.typesByName().values();
}
/** Returns the named type. */
public Schema getType(String name) {
- return types.get(name);
+ Schema namedSchema = null;
+ if (!name.contains(".")) {
+ namedSchema = context.getNamedSchema(namespace + "." + name);
+ }
+ return namedSchema != null ? namedSchema : context.getNamedSchema(name);
}
/** Set the types of this protocol. */
public void setTypes(Collection newTypes) {
- types = new Schema.Names();
+ context = new ParseContext();
for (Schema s : newTypes)
- types.add(s);
+ context.put(s);
+ context.commit();
}
/** The messages of this protocol. */
@@ -349,12 +360,12 @@ public Message createMessage(Message m, Schema request) {
}
/** Create a one-way message. */
- public Message createMessage(String name, String doc, JsonProperties propMap, Schema request) {
+ public Message createMessage(String name, String doc, JsonProperties propMap, Schema request) {
return new Message(name, doc, propMap, request);
}
/** Create a one-way message. */
- public Message createMessage(String name, String doc, Map propMap, Schema request) {
+ public Message createMessage(String name, String doc, Map propMap, Schema request) {
return new Message(name, doc, propMap, request);
}
@@ -373,13 +384,13 @@ public Message createMessage(Message m, Schema request, Schema response, Schema
}
/** Create a two-way message. */
- public Message createMessage(String name, String doc, JsonProperties propMap, Schema request, Schema response,
+ public Message createMessage(String name, String doc, JsonProperties propMap, Schema request, Schema response,
Schema errors) {
return new TwoWayMessage(name, doc, propMap, request, response, errors);
}
/** Create a two-way message. */
- public Message createMessage(String name, String doc, Map propMap, Schema request, Schema response,
+ public Message createMessage(String name, String doc, Map propMap, Schema request, Schema response,
Schema errors) {
return new TwoWayMessage(name, doc, propMap, request, response, errors);
}
@@ -392,13 +403,13 @@ public boolean equals(Object o) {
return false;
Protocol that = (Protocol) o;
return Objects.equals(this.name, that.name) && Objects.equals(this.namespace, that.namespace)
- && Objects.equals(this.types, that.types) && Objects.equals(this.messages, that.messages)
- && this.propsEqual(that);
+ && Objects.equals(this.context.resolveAllSchemas(), that.context.resolveAllSchemas())
+ && Objects.equals(this.messages, that.messages) && this.propsEqual(that);
}
@Override
public int hashCode() {
- return 31 * Objects.hash(name, namespace, types, messages) + propsHashCode();
+ return 31 * Objects.hash(name, namespace, context, messages) + propsHashCode();
}
/** Render this as JSON. */
@@ -427,8 +438,6 @@ public String toString(boolean pretty) {
}
void toJson(JsonGenerator gen) throws IOException {
- types.space(namespace);
-
gen.writeStartObject();
gen.writeStringField("protocol", name);
if (namespace != null) {
@@ -439,16 +448,16 @@ void toJson(JsonGenerator gen) throws IOException {
gen.writeStringField("doc", doc);
writeProps(gen);
gen.writeArrayFieldStart("types");
- Schema.Names resolved = new Schema.Names(namespace);
- for (Schema type : types.values())
- if (!resolved.contains(type))
- type.toJson(resolved, gen);
+ Set knownNames = new HashSet<>();
+ for (Schema type : context.resolveAllSchemas())
+ if (!knownNames.contains(type.getFullName()))
+ type.toJson(knownNames, namespace, gen);
gen.writeEndArray();
gen.writeObjectFieldStart("messages");
for (Map.Entry e : messages.entrySet()) {
gen.writeFieldName(e.getKey());
- e.getValue().toJson(gen);
+ e.getValue().toJson(knownNames, gen);
}
gen.writeEndObject();
gen.writeEndObject();
@@ -510,6 +519,27 @@ private void parse(JsonNode json) {
parseMessages(json);
parseDoc(json);
parseProps(json);
+
+ context.commit();
+ context.resolveAllSchemas();
+ resolveMessageSchemata();
+ }
+
+ private void resolveMessageSchemata() {
+ for (Map.Entry entry : messages.entrySet()) {
+ Message oldValue = entry.getValue();
+ Message newValue;
+ if (oldValue.isOneWay()) {
+ newValue = createMessage(oldValue.getName(), oldValue.getDoc(), oldValue,
+ context.resolve(oldValue.getRequest()));
+ } else {
+ Schema request = context.resolve(oldValue.getRequest());
+ Schema response = context.resolve(oldValue.getResponse());
+ Schema errors = context.resolve(oldValue.getErrors());
+ newValue = createMessage(oldValue.getName(), oldValue.getDoc(), oldValue, request, response, errors);
+ }
+ entry.setValue(newValue);
+ }
}
private void parseNameAndNamespace(JsonNode json) {
@@ -544,11 +574,7 @@ private void parseTypes(JsonNode json) {
for (JsonNode type : defs) {
if (!type.isObject())
throw new SchemaParseException("Type not an object: " + type);
- Schema.parseNamesDeclared(type, types, types.space());
-
- }
- for (JsonNode type : defs) {
- Schema.parseCompleteSchema(type, types, types.space());
+ Schema.parse(type, context, namespace);
}
}
@@ -596,8 +622,8 @@ private Message parseMessage(String messageName, JsonNode json) {
JsonNode fieldDocNode = field.get("doc");
if (fieldDocNode != null)
fieldDoc = fieldDocNode.textValue();
- Field newField = new Field(name, Schema.parse(fieldTypeNode, types), fieldDoc, field.get("default"), true,
- Order.ASCENDING);
+ Field newField = new Field(name, Schema.parse(fieldTypeNode, context, namespace), fieldDoc, field.get("default"),
+ true, Order.ASCENDING);
Set aliases = Schema.parseAliases(field);
if (aliases != null) { // add aliases
for (String alias : aliases)
@@ -612,7 +638,7 @@ private Message parseMessage(String messageName, JsonNode json) {
}
fields.add(newField);
}
- Schema request = Schema.createRecord(fields);
+ Schema request = Schema.createRecord(null, null, null, false, fields);
boolean oneWay = false;
JsonNode oneWayNode = json.get("one-way");
@@ -631,12 +657,12 @@ private Message parseMessage(String messageName, JsonNode json) {
if (oneWay) {
if (decls != null)
throw new SchemaParseException("one-way can't have errors: " + json);
- if (responseNode != null && Schema.parse(responseNode, types).getType() != Schema.Type.NULL)
+ if (responseNode != null && Schema.parse(responseNode, context, namespace).getType() != Schema.Type.NULL)
throw new SchemaParseException("One way response must be null: " + json);
return new Message(messageName, doc, mProps, request);
}
- Schema response = Schema.parse(responseNode, types);
+ Schema response = Schema.parse(responseNode, context, namespace);
List errs = new ArrayList<>();
errs.add(SYSTEM_ERROR); // every method can throw
@@ -645,7 +671,7 @@ private Message parseMessage(String messageName, JsonNode json) {
throw new SchemaParseException("Errors not an array: " + json);
for (JsonNode decl : decls) {
String name = decl.textValue();
- Schema schema = this.types.get(name);
+ Schema schema = this.context.find(name, namespace);
if (schema == null)
throw new SchemaParseException("Undefined error: " + name);
if (!schema.isError())
@@ -660,5 +686,4 @@ private Message parseMessage(String messageName, JsonNode json) {
public static void main(String[] args) throws Exception {
System.out.println(Protocol.parse(new File(args[0])));
}
-
}
diff --git a/lang/java/avro/src/main/java/org/apache/avro/Schema.java b/lang/java/avro/src/main/java/org/apache/avro/Schema.java
index 5e62bd1100b..e38e31d4072 100644
--- a/lang/java/avro/src/main/java/org/apache/avro/Schema.java
+++ b/lang/java/avro/src/main/java/org/apache/avro/Schema.java
@@ -46,7 +46,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -55,7 +54,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
import static org.apache.avro.LogicalType.LOGICAL_TYPE_PROP;
@@ -80,9 +78,9 @@
* null.
*
*
- * A schema can be constructed using one of its static createXXX
- * methods, or more conveniently using {@link SchemaBuilder}. The schema objects
- * are logically immutable. There are only two mutating methods -
+ * Construct a schema using one of its static createXXX methods, or
+ * more conveniently using {@link SchemaBuilder}. The schema objects are
+ * logically immutable. There are only two mutating methods -
* {@link #setFields(List)} and {@link #addProp(String, String)}. The following
* restrictions apply on these two methods.
*
@@ -93,6 +91,7 @@
* property.
*
*/
+@SuppressWarnings("unused")
public abstract class Schema extends JsonProperties implements Serializable {
private static final long serialVersionUID = 1L;
@@ -125,20 +124,20 @@ private Object readResolve() {
FACTORY.setCodec(MAPPER);
}
- /** The type of a schema. */
+ /** The type of schema. */
public enum Type {
RECORD, ENUM, ARRAY, MAP, UNION, FIXED, STRING, BYTES, INT, LONG, FLOAT, DOUBLE, BOOLEAN, NULL;
private final String name;
- private Type() {
+ Type() {
this.name = this.name().toLowerCase(Locale.ENGLISH);
}
public String getName() {
return name;
}
- };
+ }
private final Type type;
private LogicalType logicalType = null;
@@ -206,9 +205,9 @@ void setLogicalType(LogicalType logicalType) {
* Create an anonymous record schema.
*
* @deprecated This method allows to create Schema objects that cannot be parsed
- * by {@link Schema.Parser#parse(String)}. It will be removed in a
- * future version of Avro. Better use
- * i{@link #createRecord(String, String, String, boolean, List)} to
+ * by {@link SchemaParser#parse(CharSequence)}. It will be removed
+ * in a future version of Avro. Better use
+ * {@link #createRecord(String, String, String, boolean, List)} to
* produce a fully qualified Schema.
*/
@Deprecated
@@ -273,7 +272,7 @@ public Type getType() {
* fieldName. If there is no field by that name, a null is
* returned.
*/
- public Field getField(String fieldname) {
+ public Field getField(String fieldName) {
throw new AvroRuntimeException("Not a record: " + this);
}
@@ -406,7 +405,7 @@ public String toString() {
* @param pretty if true, pretty-print JSON.
*/
public String toString(boolean pretty) {
- return toString(new Names(), pretty);
+ return toString(new HashSet(), pretty);
}
/**
@@ -419,22 +418,22 @@ public String toString(boolean pretty) {
// Use at your own risk. This method should be removed with AVRO-2832.
@Deprecated
public String toString(Collection referencedSchemas, boolean pretty) {
- Schema.Names names = new Schema.Names();
+ Set knownNames = new HashSet<>();
if (referencedSchemas != null) {
for (Schema s : referencedSchemas) {
- names.add(s);
+ knownNames.add(s.getFullName());
}
}
- return toString(names, pretty);
+ return toString(knownNames, pretty);
}
- String toString(Names names, boolean pretty) {
+ String toString(Set knownNames, boolean pretty) {
try {
StringWriter writer = new StringWriter();
JsonGenerator gen = FACTORY.createGenerator(writer);
if (pretty)
gen.useDefaultPrettyPrinter();
- toJson(names, gen);
+ toJson(knownNames, null, gen);
gen.flush();
return writer.toString();
} catch (IOException e) {
@@ -442,7 +441,7 @@ String toString(Names names, boolean pretty) {
}
}
- void toJson(Names names, JsonGenerator gen) throws IOException {
+ void toJson(Set knownNames, String namespace, JsonGenerator gen) throws IOException {
if (!hasProps()) { // no props defined
gen.writeString(getName()); // just write name
} else {
@@ -453,7 +452,7 @@ void toJson(Names names, JsonGenerator gen) throws IOException {
}
}
- void fieldsToJson(Names names, JsonGenerator gen) throws IOException {
+ void fieldsToJson(Set knownNames, String namespace, JsonGenerator gen) throws IOException {
throw new AvroRuntimeException("Not a record: " + this);
}
@@ -487,12 +486,12 @@ final boolean equalCachedHash(Schema other) {
private static final Set FIELD_RESERVED = Collections
.unmodifiableSet(new HashSet<>(Arrays.asList("default", "doc", "name", "order", "type", "aliases")));
- /** Returns true if this record is an union type. */
+ /** Returns true if this record is a union type. */
public boolean isUnion() {
return this instanceof UnionSchema;
}
- /** Returns true if this record is an union type containing null. */
+ /** Returns true if this record is a union type containing null. */
public boolean isNullable() {
if (!isUnion()) {
return getType().equals(Schema.Type.NULL);
@@ -581,14 +580,14 @@ public Field(Field field, Schema schema) {
*
*/
public Field(String name, Schema schema) {
- this(name, schema, (String) null, (JsonNode) null, true, Order.ASCENDING);
+ this(name, schema, null, null, true, Order.ASCENDING);
}
/**
*
*/
public Field(String name, Schema schema, String doc) {
- this(name, schema, doc, (JsonNode) null, true, Order.ASCENDING);
+ this(name, schema, doc, null, true, Order.ASCENDING);
}
/**
@@ -613,7 +612,7 @@ public Field(String name, Schema schema, String doc, Object defaultValue, Order
public String name() {
return name;
- };
+ }
/** The position of this field within the record. */
public int pos() {
@@ -698,95 +697,6 @@ private boolean defaultValueEquals(JsonNode thatDefaultValue) {
public String toString() {
return name + " type:" + schema.type + " pos:" + position;
}
-
- /**
- * Parse field.
- *
- * @param field : json field definition.
- * @param names : names map.
- * @param namespace : current working namespace.
- * @return field.
- */
- static Field parse(JsonNode field, Names names, String namespace) {
- String fieldName = getRequiredText(field, "name", "No field name");
- String fieldDoc = getOptionalText(field, "doc");
- JsonNode fieldTypeNode = field.get("type");
- if (fieldTypeNode == null) {
- throw new SchemaParseException("No field type: " + field);
- }
-
- Schema fieldSchema = null;
- if (fieldTypeNode.isTextual()) {
- Schema schemaField = names.get(fieldTypeNode.textValue());
- if (schemaField == null) {
- schemaField = names.get(namespace + "." + fieldTypeNode.textValue());
- }
- if (schemaField == null) {
- throw new SchemaParseException(fieldTypeNode + " is not a defined name." + " The type of the \"" + fieldName
- + "\" field must be a defined name or a {\"type\": ...} expression.");
- }
- fieldSchema = schemaField;
- } else if (fieldTypeNode.isObject()) {
- fieldSchema = resolveSchema(fieldTypeNode, names, namespace);
- if (fieldSchema == null) {
- fieldSchema = Schema.parseCompleteSchema(fieldTypeNode, names, namespace);
- }
- } else if (fieldTypeNode.isArray()) {
- List unionTypes = new ArrayList<>();
-
- fieldTypeNode.forEach((JsonNode node) -> {
- Schema subSchema = null;
- if (node.isTextual()) {
- subSchema = names.get(node.asText());
- if (subSchema == null) {
- subSchema = names.get(namespace + "." + node.asText());
- }
- } else if (node.isObject()) {
- subSchema = Schema.parseCompleteSchema(node, names, namespace);
- } else {
- throw new SchemaParseException("Illegal type in union : " + node);
- }
- if (subSchema == null) {
- throw new SchemaParseException("Null element in union : " + node);
- }
- unionTypes.add(subSchema);
- });
-
- fieldSchema = Schema.createUnion(unionTypes);
- }
-
- if (fieldSchema == null) {
- throw new SchemaParseException("Can't find type for field " + fieldName);
- }
- Field.Order order = Field.Order.ASCENDING;
- JsonNode orderNode = field.get("order");
- if (orderNode != null)
- order = Field.Order.valueOf(orderNode.textValue().toUpperCase(Locale.ENGLISH));
- JsonNode defaultValue = field.get("default");
-
- if (defaultValue != null
- && (Type.FLOAT.equals(fieldSchema.getType()) || Type.DOUBLE.equals(fieldSchema.getType()))
- && defaultValue.isTextual()) {
- try {
- defaultValue = new DoubleNode(Double.valueOf(defaultValue.textValue()));
- } catch (NumberFormatException ex) {
- throw new SchemaParseException(
- "Can't parse number '" + defaultValue.textValue() + "' for field '" + fieldName);
- }
- }
-
- Field f = new Field(fieldName, fieldSchema, fieldDoc, defaultValue, true, order);
- Iterator i = field.fieldNames();
- while (i.hasNext()) { // add field props
- String prop = i.next();
- if (!FIELD_RESERVED.contains(prop))
- f.addProp(prop, field.get(prop));
- }
- f.aliases = parseAliases(field);
-
- return f;
- }
-
}
static class Name {
@@ -832,13 +742,13 @@ public String toString() {
return full;
}
- public void writeName(Names names, JsonGenerator gen) throws IOException {
+ public void writeName(String currentNamespace, JsonGenerator gen) throws IOException {
if (name != null)
gen.writeStringField("name", name);
if (space != null) {
- if (!space.equals(names.space()))
+ if (!space.equals(currentNamespace))
gen.writeStringField("namespace", space);
- } else if (names.space() != null) { // null within non-null
+ } else if (currentNamespace != null) { // null within non-null
gen.writeStringField("namespace", "");
}
}
@@ -849,8 +759,8 @@ public String getQualified(String defaultSpace) {
/**
* Determine if full name must be written. There are 2 cases for true :
- * defaultSpace != from this.space or name is already a Schema.Type (int, array
- * ...)
+ * {@code defaultSpace} != from {@code this.space} or name is already a
+ * {@code Schema.Type} (int, array, ...)
*
* @param defaultSpace : default name space.
* @return true if full name must be written.
@@ -925,22 +835,25 @@ public Set getAliases() {
Set result = new LinkedHashSet<>();
if (aliases != null)
for (Name alias : aliases)
- result.add(alias.full);
+ if (alias.space == null && name.space != null)
+ result.add("." + alias.name);
+ else
+ result.add(alias.full);
return result;
}
- public boolean writeNameRef(Names names, JsonGenerator gen) throws IOException {
- if (this.equals(names.get(name))) {
- gen.writeString(name.getQualified(names.space()));
- return true;
- } else if (name.name != null) {
- names.put(name, this);
+ public boolean writeNameRef(Set knownNames, String currentNamespace, JsonGenerator gen) throws IOException {
+ if (name.name != null) {
+ if (!knownNames.add(name.full)) {
+ gen.writeString(name.getQualified(currentNamespace));
+ return true;
+ }
}
return false;
}
- public void writeName(Names names, JsonGenerator gen) throws IOException {
- name.writeName(names, gen);
+ public void writeName(String currentNamespace, JsonGenerator gen) throws IOException {
+ name.writeName(currentNamespace, gen);
}
public boolean equalNames(NamedSchema that) {
@@ -969,8 +882,8 @@ public void aliasesToJson(JsonGenerator gen) throws IOException {
* and need to watch for recursion.
*/
public static class SeenPair {
- private Object s1;
- private Object s2;
+ private final Object s1;
+ private final Object s2;
public SeenPair(Object s1, Object s2) {
this.s1 = s1;
@@ -992,7 +905,6 @@ public int hashCode() {
private static final ThreadLocal> SEEN_EQUALS = ThreadLocalWithInitial.of(HashSet::new);
private static final ThreadLocal