CStructDescriptor.java
/*
* Copyright 2016 Providence Authors
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.morimekta.providence.reflect.contained;
import net.morimekta.providence.PMessageBuilder;
import net.morimekta.providence.descriptor.PAnnotation;
import net.morimekta.providence.descriptor.PDescriptorProvider;
import net.morimekta.providence.descriptor.PRequirement;
import net.morimekta.providence.descriptor.PStructDescriptor;
import net.morimekta.providence.serializer.json.JsonCompactibleDescriptor;
import net.morimekta.util.collect.UnmodifiableMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
/**
* @author Stein Eldar Johnsen
* @since 07.09.15
*/
public class CStructDescriptor extends PStructDescriptor<CStruct>
implements CMessageDescriptor, JsonCompactibleDescriptor {
public static final int MAX_COMPACT_FIELDS = 10;
private final String comment;
private final CField<CStruct>[] fields;
private final Map<Integer, CField<CStruct>> fieldIdMap;
private final Map<String, CField<CStruct>> fieldNameMap;
private final Map<String, CField<CStruct>> fieldPojoNameMap;
private final Map<String, String> annotations;
private final boolean compactible;
private final PDescriptorProvider implementing;
private final boolean innerType;
private final boolean autoType;
@SuppressWarnings("unchecked")
public CStructDescriptor(String comment,
String programName,
String name,
List<CField<CStruct>> fields,
Map<String, String> annotations,
PDescriptorProvider implementing) {
super(programName, name, new _Factory(), false);
((_Factory) getBuilderSupplier()).setType(this);
this.innerType = name.contains(".");
// TODO: Make better detection of auto-types. Currently only service RQ / RS.
this.autoType = innerType && name.indexOf(".") != name.lastIndexOf(".");
this.comment = comment;
this.fields = fields.toArray((CField<CStruct>[]) CField.EMPTY_ARRAY);
this.annotations = annotations;
this.compactible = isCompactCompatible(fields, annotations);
UnmodifiableMap.Builder<Integer, CField<CStruct>> fieldIdMap = UnmodifiableMap.builder(this.fields.length);
UnmodifiableMap.Builder<String, CField<CStruct>> fieldNameMap = UnmodifiableMap.builder(this.fields.length);
UnmodifiableMap.Builder<String, CField<CStruct>> fieldPojoNameMap = UnmodifiableMap.builder(this.fields.length);
for (CField<CStruct> field : fields) {
field.setMessageType(this);
fieldIdMap.put(field.getId(), field);
fieldNameMap.put(field.getName(), field);
fieldPojoNameMap.put(field.getPojoName(), field);
}
this.fieldIdMap = fieldIdMap.build();
this.fieldNameMap = fieldNameMap.build();
this.fieldPojoNameMap = fieldPojoNameMap.build();
this.implementing = implementing;
}
@Override
public boolean isInnerType() {
return innerType;
}
@Override
public boolean isAutoType() {
return autoType;
}
@Override
public final String getDocumentation() {
return comment;
}
@Nonnull
@Override
public CField<CStruct>[] getFields() {
return Arrays.copyOf(fields, fields.length);
}
@Override
public CField<CStruct> findFieldByName(String name) {
return fieldNameMap.get(name);
}
@Override
public CField<CStruct> findFieldByPojoName(String pojoName) {
return fieldPojoNameMap.get(pojoName);
}
@Override
public CField<CStruct> findFieldById(int id) {
return fieldIdMap.get(id);
}
@Nullable
@Override
public CInterfaceDescriptor getImplementing() {
if (implementing == null) return null;
return (CInterfaceDescriptor) implementing.descriptor();
}
@Nonnull
@Override
@SuppressWarnings("unchecked")
public Set<String> getAnnotations() {
if (annotations != null) {
return annotations.keySet();
}
return Collections.EMPTY_SET;
}
@Override
public boolean hasAnnotation(@Nonnull String name) {
if (annotations != null) {
return annotations.containsKey(name);
}
return false;
}
@Override
public String getAnnotationValue(@Nonnull String name) {
if (annotations != null) {
return annotations.get(name);
}
return null;
}
@Override
public boolean isSimple() {
for (CField<?> field : getFields()) {
switch (field.getType()) {
case MAP:
case SET:
case LIST:
case MESSAGE:
return false;
default:
break;
}
}
return true;
}
@Override
public boolean isJsonCompactible() {
return compactible;
}
private static class _Factory implements Supplier<PMessageBuilder<CStruct>> {
private CStructDescriptor mType;
public void setType(CStructDescriptor type) {
mType = type;
}
@Nonnull
@Override
public PMessageBuilder<CStruct> get() {
return new CStruct.Builder(mType);
}
}
private static boolean isCompactCompatible(List<CField<CStruct>> fields, Map<String, String> annotations) {
if (annotations == null) {
return false;
}
if (!annotations.containsKey(PAnnotation.JSON_COMPACT.tag) &&
// legacy annotation version special handling.
!annotations.containsKey("compact")) {
return false;
}
if (fields.size() > MAX_COMPACT_FIELDS) {
return false;
}
int next = 1;
boolean hasOptional = false;
for (CField<?> field : fields) {
if (field.getId() != next) {
return false;
}
if (hasOptional && field.getRequirement() == PRequirement.REQUIRED) {
return false;
}
if (field.getRequirement() == PRequirement.OPTIONAL) {
hasOptional = true;
}
next++;
}
return true;
}
}