/* * Copyright (c) 2020 PANTHEON.tech, s.r.o. 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.yangtools.yang.model.spi.meta; import static java.util.Objects.requireNonNull; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.common.Empty; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace; import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement; /** * Baseline stateless implementation of an EffectiveStatement. This class adds a few default implementations and * namespace dispatch, but does not actually force any state on its subclasses. This approach adds requirements for an * implementation, but it leaves it up to the final class to provide object layout. * *

* This finds immense value in catering the common case, for example effective statements which can, but typically * do not, contain substatements. * * @param Argument type ({@link Empty} if statement does not have argument.) * @param Class representing declared version of this statement. */ abstract class AbstractEffectiveStatement> extends AbstractModelStatement implements EffectiveStatement { @Override public final > Optional get(final Class namespace, final K identifier) { return Optional.ofNullable(getAll(namespace).get(requireNonNull(identifier))); } @Override public final > Map getAll(final Class namespace) { final Optional> ret = getNamespaceContents(requireNonNull(namespace)); return ret.isPresent() ? ret.get() : ImmutableMap.of(); } @Override public Collection> effectiveSubstatements() { return ImmutableList.of(); } /** * Return the statement-specific contents of specified namespace, if available. * * @param namespace Requested namespace * @return Namespace contents, if available. */ protected > Optional> getNamespaceContents( final @NonNull Class namespace) { return Optional.empty(); } /** * Utility method for recovering singleton lists squashed by {@link #maskList(ImmutableList)}. * * @param masked list to unmask * @return Unmasked list * @throws NullPointerException if masked is null * @throws ClassCastException if masked object does not match EffectiveStatement */ @SuppressWarnings({ "rawtypes", "unchecked" }) protected static final @NonNull ImmutableList> unmaskList( final @NonNull Object masked) { return (ImmutableList) unmaskList(masked, EffectiveStatement.class); } // TODO: below methods need to find a better place, this is just a temporary hideout as their public class is on // its way out protected static @NonNull Map> createSchemaTreeNamespace( final Collection> substatements) { final Map> schemaChildren = new LinkedHashMap<>(); substatements.stream().filter(SchemaTreeEffectiveStatement.class::isInstance) .forEach(child -> putChild(schemaChildren, (SchemaTreeEffectiveStatement) child, "schema")); return schemaChildren; } protected static @NonNull ImmutableMap> createDataTreeNamespace( final Collection> schemaTreeStatements, // Note: this dance is needed to not retain ImmutableMap$Values final ImmutableMap> schemaTreeNamespace) { final Map> dataChildren = new LinkedHashMap<>(); boolean sameAsSchema = true; for (SchemaTreeEffectiveStatement child : schemaTreeStatements) { if (!indexDataTree(dataChildren, child)) { sameAsSchema = false; } } // This is a mighty hack to lower memory usage: if we consumed all schema tree children as data nodes, // the two maps are equal and hence we can share the instance. return sameAsSchema ? (ImmutableMap) schemaTreeNamespace : ImmutableMap.copyOf(dataChildren); } private static boolean indexDataTree(final Map> map, final EffectiveStatement stmt) { if (stmt instanceof DataTreeEffectiveStatement) { putChild(map, (DataTreeEffectiveStatement) stmt, "data"); return true; } else if (stmt instanceof ChoiceEffectiveStatement) { // For choice statements go through all their cases and fetch their data children for (EffectiveStatement choiceChild : stmt.effectiveSubstatements()) { if (choiceChild instanceof CaseEffectiveStatement) { for (EffectiveStatement caseChild : choiceChild.effectiveSubstatements()) { indexDataTree(map, caseChild); } } } } else if (stmt instanceof CaseEffectiveStatement) { // For case statements go through all their statements for (EffectiveStatement child : stmt.effectiveSubstatements()) { indexDataTree(map, child); } } return false; } private static > void putChild(final Map map, final T child, final String tree) { final QName id = child.getIdentifier(); final T prev = map.putIfAbsent(id, child); if (prev != null) { throw new SubstatementIndexingException( "Cannot add " + tree + " tree child with name " + id + ", a conflicting child already exists"); } } }