/* * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.mdsal.binding.generator.impl; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.binding.model.api.GeneratedType; import org.opendaylight.mdsal.binding.model.api.JavaTypeName; import org.opendaylight.mdsal.binding.model.api.Type; import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder; import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder; import org.opendaylight.mdsal.binding.spec.naming.BindingMapping; import org.opendaylight.yangtools.concepts.Mutable; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus; import org.opendaylight.yangtools.yang.model.api.GroupingDefinition; import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.OperationDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility class for building up Binding mapping information. This class is NOT thread-safe. */ public final class ModuleContext implements Mutable { private static final Logger LOG = LoggerFactory.getLogger(ModuleContext.class); private final BiMap typeToAugmentation = HashBiMap.create(); private final Map childNodes = new HashMap<>(); private final Map groupings = new HashMap<>(); private final BiMap caseTypeToSchema = HashBiMap.create(); private final Map cases = new HashMap<>(); private final Map identities = new HashMap<>(); private final List augmentations = new ArrayList<>(); private final Multimap choiceToCases = HashMultimap.create(); private final Set topLevelNodes = new HashSet<>(); private final Map aliases = new HashMap<>(); private final Map typeToSchema = new HashMap<>(); private final List genTOs = new ArrayList<>(); private final Map innerTypes = new HashMap<>(); private final Map typedefs = new HashMap<>(); private final Module module; // Conflict mapping private final Map nameMapping = new HashMap<>(); private GeneratedTypeBuilder moduleNode; private JavaTypeName moduleInfoType; private String modulePackageName; ModuleContext(final Module module) { this.module = requireNonNull(module); } Module module() { return module; } @NonNull String modulePackageName() { String ret = modulePackageName; if (ret == null) { modulePackageName = ret = BindingMapping.getRootPackageName(module.getQNameModule()); } return ret; } @NonNull JavaTypeName moduleInfoType() { JavaTypeName ret = moduleInfoType; if (ret == null) { moduleInfoType = ret = JavaTypeName.create(modulePackageName(), BindingMapping.MODULE_INFO_CLASS_NAME); } return ret; } List getGeneratedTypes() { List result = new ArrayList<>(); if (moduleNode != null) { result.add(moduleNode.build()); } for (GeneratedTOBuilder b : genTOs) { result.add(b.build()); } for (Type b : typedefs.values()) { if (b != null) { result.add(b); } } for (GeneratedTypeBuilder b : childNodes.values()) { result.add(b.build()); } for (GeneratedTypeBuilder b : groupings.values()) { result.add(b.build()); } for (GeneratedTypeBuilder b : cases.values()) { result.add(b.build()); } for (GeneratedTypeBuilder b : identities.values()) { result.add(b.build()); } for (GeneratedTypeBuilder b : topLevelNodes) { result.add(b.build()); } for (GeneratedTypeBuilder b : augmentations) { result.add(b.build()); } return result; } public Multimap getChoiceToCases() { return Multimaps.unmodifiableMultimap(choiceToCases); } public GeneratedTypeBuilder getModuleNode() { return moduleNode; } public GeneratedTypeBuilder getChildNode(final SchemaPath path) { return childNodes.get(path); } public GeneratedTypeBuilder getGrouping(final SchemaPath path) { return groupings.get(path); } public GeneratedTypeBuilder getCase(final SchemaPath path) { return cases.get(path); } public void addModuleNode(final GeneratedTypeBuilder newModuleNode) { this.moduleNode = newModuleNode; } public void addGeneratedTOBuilder(final GeneratedTOBuilder builder) { genTOs.add(builder); } @NonNull GeneratedType addAliasType(final ModuleContext sourceContext, final ContainerSchemaNode source, final ContainerSchemaNode alias) { final GeneratedTypeBuilder builder = sourceContext.getChildNode(source.getPath()); checkState(builder != null, "Could not find builder for %s", source); final JavaTypeName id = builder.getIdentifier(); final SchemaPath path = alias.getPath(); final JavaTypeName prev = aliases.putIfAbsent(path, id); checkState(prev == null, "Type aliasing conflict on %s: %s vs %s", path, prev, id); return builder.build(); } @Nullable JavaTypeName getAlias(final SchemaPath path) { return aliases.get(path); } public void addChildNodeType(final SchemaNode def, final GeneratedTypeBuilder builder) { checkNamingConflict(def, builder.getIdentifier()); childNodes.put(def.getPath(), builder); typeToSchema.put(builder, def); } public void addGroupingType(final GroupingDefinition def, final GeneratedTypeBuilder builder) { checkNamingConflict(def, builder.getIdentifier()); groupings.put(def.getPath(), builder); } public void addTypedefType(final TypeDefinition def, final Type type) { final JavaTypeName name = type.getIdentifier(); final SchemaNode existingDef = nameMapping.putIfAbsent(name, def); if (existingDef != null) { if (!(existingDef instanceof TypeDefinition)) { throw resolveNamingConfict(existingDef, def, name); } // This seems to be fine LOG.debug("GeneratedType conflict between {} and {} on {}", def, existingDef, name); } typedefs.put(def.getPath(), type); } public void addCaseType(final SchemaPath path, final GeneratedTypeBuilder builder) { cases.put(path, builder); } public void addIdentityType(final IdentitySchemaNode def, final GeneratedTypeBuilder builder) { checkNamingConflict(def, builder.getIdentifier()); identities.put(def.getQName(), builder); } public void addTopLevelNodeType(final GeneratedTypeBuilder builder) { topLevelNodes.add(builder); } public void addAugmentType(final GeneratedTypeBuilder builder) { augmentations.add(builder); } public Map getTypedefs() { return typedefs; } public Map getChildNodes() { return Collections.unmodifiableMap(childNodes); } public Map getGroupings() { return Collections.unmodifiableMap(groupings); } public Map getCases() { return Collections.unmodifiableMap(cases); } public Map getIdentities() { return Collections.unmodifiableMap(identities); } public Set getTopLevelNodes() { return Collections.unmodifiableSet(topLevelNodes); } public List getAugmentations() { return Collections.unmodifiableList(augmentations); } public BiMap getTypeToAugmentation() { return Maps.unmodifiableBiMap(typeToAugmentation); } public void addTypeToAugmentation(final GeneratedTypeBuilder builder, final AugmentationSchemaNode schema) { typeToAugmentation.put(builder, schema); typeToSchema.put(builder, schema); } public void addChoiceToCaseMapping(final Type choiceType, final Type caseType, final CaseSchemaNode schema) { choiceToCases.put(choiceType, caseType); caseTypeToSchema.put(caseType, schema); typeToSchema.put(caseType, schema); } public BiMap getCaseTypeToSchemas() { return Maps.unmodifiableBiMap(caseTypeToSchema); } /** * Returns mapping of type to its schema. Valid values are only instances of {@link DataSchemaNode} * or {@link AugmentationSchemaNode}. * * @return Mapping from type to corresponding schema */ public Map getTypeToSchema() { return Collections.unmodifiableMap(typeToSchema); } protected void addTypeToSchema(final Type type, final TypeDefinition typedef) { typeToSchema.put(type, typedef); } /** * Adds mapping between schema path and an inner type. */ void addInnerTypedefType(final SchemaPath path, final Type type) { innerTypes.put(path, type); } public Type getInnerType(final SchemaPath path) { return innerTypes.get(path); } private void checkNamingConflict(final SchemaNode def, final JavaTypeName name) { final SchemaNode existingDef = nameMapping.putIfAbsent(name, def); if (existingDef != null) { if (def.equals(existingDef)) { if (LOG.isDebugEnabled()) { // TODO: this should not really be happening LOG.debug("Duplicate definition on {} as {}", name, def, new Throwable()); } } else { throw resolveNamingConfict(existingDef, def, name); } } } private static IllegalStateException resolveNamingConfict(final SchemaNode existing, final SchemaNode incoming, final JavaTypeName name) { if (existing instanceof IdentitySchemaNode) { if (incoming instanceof GroupingDefinition || incoming instanceof TypeDefinition || incoming instanceof DataSchemaNode || incoming instanceof NotificationDefinition || incoming instanceof OperationDefinition) { return new RenameMappingException(name, existing); } } else if (existing instanceof GroupingDefinition) { if (incoming instanceof IdentitySchemaNode) { return new RenameMappingException(name, incoming); } if (incoming instanceof TypeDefinition || incoming instanceof DataSchemaNode || incoming instanceof NotificationDefinition || incoming instanceof OperationDefinition) { return new RenameMappingException(name, existing); } } else if (existing instanceof TypeDefinition) { if (incoming instanceof GroupingDefinition || incoming instanceof IdentitySchemaNode) { return new RenameMappingException(name, incoming); } if (incoming instanceof DataSchemaNode || incoming instanceof NotificationDefinition || incoming instanceof OperationDefinition) { return new RenameMappingException(name, existing); } } else { if (incoming instanceof GroupingDefinition || incoming instanceof IdentitySchemaNode || incoming instanceof TypeDefinition) { return new RenameMappingException(name, incoming); } } return new IllegalStateException(String.format("Unhandled GeneratedType conflict between %s and %s on %s", incoming, existing, name)); } }