package jetbrains.mps.vcs.diff.merge;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.vcs.diff.ChangeSet;
import java.util.Map;
import jetbrains.mps.vcs.diff.changes.ModelChange;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import org.jetbrains.mps.openapi.model.SNodeId;
import jetbrains.mps.vcs.diff.ChangeSetBuilder;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.Collection;
import jetbrains.mps.vcs.diff.changes.HierarchicalNodeGroupChange;
import java.util.Objects;
import jetbrains.mps.baseLanguage.tuples.runtime.Tuples;
import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple;
import jetbrains.mps.internal.collections.runtime.CollectionSequence;
import java.util.Collections;
import java.util.ArrayList;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import java.util.Iterator;
import jetbrains.mps.vcs.diff.changes.NodeGroupMoveChange;
import jetbrains.mps.vcs.diff.changes.NodeGroupNotMoveChange;
import jetbrains.mps.vcs.diff.changes.NodeGroupWrapChange;
import java.util.Set;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.vcs.diff.DiffUtil;
import jetbrains.mps.vcs.diff.changes.WrappingNodesGroup;
import java.util.function.Function;
import jetbrains.mps.internal.collections.runtime.IListSequence;

@GeneratedClass(node = "r:e9c4e128-4808-4224-a92b-dbeed02eb860(jetbrains.mps.vcs.diff.merge)/2244785825716135804", model = "r:e9c4e128-4808-4224-a92b-dbeed02eb860(jetbrains.mps.vcs.diff.merge)")
public class MovesAwareMergeConflictsBuilder implements ChangeConflictsBuilder {

  @NotNull
  private final SModel myMineModel;
  @NotNull
  private final SModel myRepositoryModel;
  @NotNull
  private final ChangeSet myMineChangeSet;
  @NotNull
  private final ChangeSet myRepositoryChangeSet;
  private final Map<ModelChange, List<ModelChange>> myConflictingChanges = MapSequence.fromMap(new HashMap<ModelChange, List<ModelChange>>());
  private final Map<ModelChange, List<ModelChange>> mySymmetricChanges = MapSequence.fromMap(new HashMap<ModelChange, List<ModelChange>>());
  private final Map<SNodeId, SNodeId> mySymmetricNodeIds = MapSequence.fromMap(new HashMap<SNodeId, SNodeId>());
  private final boolean myWrapConflictsWithInternalChanges;


  public MovesAwareMergeConflictsBuilder(@NotNull SModel base, @NotNull SModel mine, @NotNull SModel repository, boolean wrapConflictsWithInternalChanges) {
    // should be invoked from read action
    myMineModel = mine;
    myRepositoryModel = repository;
    myMineChangeSet = ChangeSetBuilder.buildChangeSet(base, mine, false, true);
    myRepositoryChangeSet = ChangeSetBuilder.buildChangeSet(base, repository, false, true);
    myWrapConflictsWithInternalChanges = wrapConflictsWithInternalChanges;
    collectConflicts();
  }

  private MovesAwareMergeConflictsBuilder(@NotNull ChangeSet mineChangeSet, @NotNull ChangeSet repoChangeSet) {
    // should be invoked from read action
    myMineModel = mineChangeSet.getNewModel();
    myRepositoryModel = repoChangeSet.getNewModel();
    myMineChangeSet = mineChangeSet;
    myRepositoryChangeSet = repoChangeSet;
    myWrapConflictsWithInternalChanges = false;
    collectConflicts();
  }

  public static MovesAwareMergeConflictsBuilder createOppositeConflictsBuilder(ChangeSet mineChangeSet, ChangeSet repoChangeSet) {
    return new MovesAwareMergeConflictsBuilder(mineChangeSet, repoChangeSet);
  }

  @Nullable
  private SNode getMyNode(@NotNull SNodeId nodeId) {
    return myMineModel.getNode(nodeId);
  }

  @Nullable
  private SNode getRepoNode(@NotNull SNodeId nodeId) {
    return myRepositoryModel.getNode(nodeId);
  }

  private void collectSymmetricHierarchicalChanges() {
    List<SNodeId> mineRootIds = ListSequence.fromList(SModelOperations.roots(myMineModel, null)).select((it) -> it.getNodeId()).toList();
    List<SNodeId> repoRootIds = ListSequence.fromList(SModelOperations.roots(myRepositoryModel, null)).select((it) -> it.getNodeId()).toList();
    ListSequence.fromList(mineRootIds).intersect(ListSequence.fromList(repoRootIds)).visitAll((rootId) -> buildSymmetricChangesForNode(getMyNode(rootId), getRepoNode(rootId)));
  }

  private void buildSymmetricChangesForNode(@NotNull final SNode myNode, @NotNull final SNode repoNode) {

    final Map<SContainmentLink, List<SNodeId>> myRoleToChildCollection = getRoleToChildCollectionMap(myNode);
    final Map<SContainmentLink, List<SNodeId>> repoRoleToChildCollection = getRoleToChildCollectionMap(repoNode);

    SetSequence.fromSet(MapSequence.fromMap(myRoleToChildCollection).keySet()).intersect(SetSequence.fromSet(MapSequence.fromMap(repoRoleToChildCollection).keySet())).visitAll((role) -> buildSymmetricChangesForNodeRole(MapSequence.fromMap(myRoleToChildCollection).get(role), MapSequence.fromMap(repoRoleToChildCollection).get(role), myNode.getNodeId(), repoNode.getNodeId(), role));
  }

  private Collection<HierarchicalNodeGroupChange> getChangesInRole(ChangeSet changeSet, final SNodeId parentId, final SContainmentLink role) {
    return ListSequence.fromList(changeSet.getModelChanges()).ofType(HierarchicalNodeGroupChange.class).where((it) -> Objects.equals(it.getParentId(true), parentId) && Objects.equals(it.getLink(true), role)).toList();
  }

  private void buildSymmetricChangesForNodeRole(@NotNull List<SNodeId> myChildrenIds, @NotNull List<SNodeId> repoChildrenIds, @NotNull SNodeId myParentId, @NotNull SNodeId repoParentId, @NotNull SContainmentLink role) {

    Collection<HierarchicalNodeGroupChange> myChanges = getChangesInRole(myMineChangeSet, myParentId, role);
    Collection<HierarchicalNodeGroupChange> repoChanges = getChangesInRole(myRepositoryChangeSet, repoParentId, role);

    SNodeId myNextNodeId = null;
    SNodeId repoNextNodeId = null;

    while (true) {

      List<HierarchicalNodeGroupChange> mySuccessiveChanges = getSuccessiveChanges(myChanges, myNextNodeId);
      List<HierarchicalNodeGroupChange> repoSuccessiveChanges = getSuccessiveChanges(repoChanges, repoNextNodeId);

      buildSymmetricChangesForAnchor(mySuccessiveChanges, repoSuccessiveChanges);

      SNodeId myFirstChangedId = getFirstChangedId(mySuccessiveChanges, myNextNodeId);
      SNodeId repoFirstChangedId = getFirstChangedId(repoSuccessiveChanges, repoNextNodeId);

      {
        Tuples._2<SNodeId, SNodeId> _tmp_pu2a0b_j0h0bb = getNextAnchorIds(myChildrenIds, repoChildrenIds, myFirstChangedId, repoFirstChangedId);
        myNextNodeId = _tmp_pu2a0b_j0h0bb._0();
        repoNextNodeId = _tmp_pu2a0b_j0h0bb._1();
      }

      if (myNextNodeId == null || repoNextNodeId == null) {
        break;
      }

      SNode myNode = getMyNode(myNextNodeId);
      SNode repoNode = getRepoNode(repoNextNodeId);
      if (myNode != null && repoNode != null) {
        buildSymmetricChangesForNode(myNode, repoNode);
      }
    }
  }

  @Nullable
  private Tuples._2<SNodeId, SNodeId> getNextAnchorIds(@NotNull List<SNodeId> myChildrenIds, @NotNull List<SNodeId> repoChildrenIds, SNodeId myFirstChangedId, SNodeId repoFirstChangedId) {

    int myFirstChangedIndex = getIdIndexInList(myChildrenIds, myFirstChangedId);
    int repoFirstChangedIndex = getIdIndexInList(repoChildrenIds, repoFirstChangedId);

    if (myFirstChangedIndex == 0 || repoFirstChangedIndex == 0) {
      return MultiTuple.<SNodeId,SNodeId>from((SNodeId) null, (SNodeId) null);
    }

    SNodeId myUnchangedId = ListSequence.fromList(myChildrenIds).getElement(myFirstChangedIndex - 1);
    SNodeId repoUnchangedId = ListSequence.fromList(repoChildrenIds).getElement(repoFirstChangedIndex - 1);
    Tuples._2<SNodeId, SNodeId> unchangedIds = getUnchangedIds(myUnchangedId, repoChildrenIds, repoFirstChangedIndex, true);
    if (unchangedIds == null) {
      unchangedIds = getUnchangedIds(repoUnchangedId, myChildrenIds, myFirstChangedIndex, false);
    }
    return (unchangedIds != null ? unchangedIds : getNextAnchorIds(myChildrenIds, repoChildrenIds, myUnchangedId, repoUnchangedId));
  }

  private static int getIdIndexInList(@NotNull List<SNodeId> ids, SNodeId id) {
    return (id == null ? ListSequence.fromList(ids).count() : ListSequence.fromList(ids).indexOf(id));
  }

  private SNodeId getSymmetricNodeId(final SNodeId nodeId, boolean isMine) {
    if (isMine) {
      return MapSequence.fromMap(mySymmetricNodeIds).get(nodeId);
    }
    if (MapSequence.fromMap(mySymmetricNodeIds).containsValue(nodeId)) {
      return MapSequence.fromMap(mySymmetricNodeIds).findFirst((it) -> Objects.equals(it.value(), nodeId)).key();
    }
    return null;
  }

  @Nullable
  private Tuples._2<SNodeId, SNodeId> getUnchangedIds(SNodeId unchangedId, List<SNodeId> otherChildrenIds, int otherFirstChangedIndex, boolean isMine) {

    if (ListSequence.fromList(otherChildrenIds).contains(unchangedId) && ListSequence.fromList(otherChildrenIds).indexOf(unchangedId) < otherFirstChangedIndex) {
      return MultiTuple.<SNodeId,SNodeId>from(unchangedId, unchangedId);
    }

    SNodeId symmetricId = getSymmetricNodeId(unchangedId, isMine);
    if (symmetricId != null && ListSequence.fromList(otherChildrenIds).contains(symmetricId) && ListSequence.fromList(otherChildrenIds).indexOf(symmetricId) < otherFirstChangedIndex) {
      return (isMine ? MultiTuple.<SNodeId,SNodeId>from(unchangedId, symmetricId) : MultiTuple.<SNodeId,SNodeId>from(symmetricId, unchangedId));
    }
    return null;
  }

  private List<HierarchicalNodeGroupChange> getSuccessiveChanges(Collection<HierarchicalNodeGroupChange> changes, final SNodeId anchorId) {
    final HierarchicalNodeGroupChange lastChange = CollectionSequence.fromCollection(changes).findFirst((it) -> it.getGroup(true).getNextGroup() == null && Objects.equals(it.getGroup(true).getNextNodeId(), anchorId));
    if (lastChange == null) {
      return Collections.emptyList();
    }
    List<HierarchicalNodeGroupChange> result = ListSequence.fromList(new ArrayList<HierarchicalNodeGroupChange>());
    ListSequence.fromList(result).addElement(lastChange);
    final Wrappers._T<HierarchicalNodeGroupChange> prevChange = new Wrappers._T<HierarchicalNodeGroupChange>(CollectionSequence.fromCollection(changes).findFirst((it) -> it.getGroup(true).getNextGroup() == lastChange.getGroup(true)));
    while (prevChange.value != null) {
      ListSequence.fromList(result).addElement(prevChange.value);
      prevChange.value = CollectionSequence.fromCollection(changes).findFirst((it) -> it.getGroup(true).getNextGroup() == prevChange.value.getGroup(true));
    }
    return result;
  }

  private void buildSymmetricChangesForAnchor(List<HierarchicalNodeGroupChange> mySuccessiveChanges, List<HierarchicalNodeGroupChange> repoSuccessiveChanges) {

    Iterator<HierarchicalNodeGroupChange> myChangeIter = ListSequence.fromList(mySuccessiveChanges).iterator();
    Iterator<HierarchicalNodeGroupChange> repoChangeIter = ListSequence.fromList(repoSuccessiveChanges).iterator();

    while (myChangeIter.hasNext() && repoChangeIter.hasNext()) {
      HierarchicalNodeGroupChange myChange = myChangeIter.next();
      HierarchicalNodeGroupChange repoChange = repoChangeIter.next();
      if (!(changesAreSymmetric(myChange, repoChange))) {
        break;
      }
      addSymmetric(myChange, repoChange);
      buildSymmetricChangesForChildren(myChange, repoChange);
    }
  }

  private boolean changesAreSymmetric(HierarchicalNodeGroupChange myChange, HierarchicalNodeGroupChange repoChange) {
    if (myChange.getClass() != repoChange.getClass()) {
      return false;
    }
    if (myChange instanceof NodeGroupMoveChange) {
      return moveChangesAreSymmetric(as_pu2a0b_a0a0a1a14(myChange, NodeGroupMoveChange.class), as_pu2a0b_a1a0a1a14(repoChange, NodeGroupMoveChange.class));
    } else if (myChange instanceof NodeGroupNotMoveChange) {
      return notMoveChangesAreSymmetric(as_pu2a0b_a0a0a0b0pb(myChange, NodeGroupNotMoveChange.class), as_pu2a0b_a1a0a0b0pb(repoChange, NodeGroupNotMoveChange.class));
    } else if (myChange instanceof NodeGroupWrapChange) {
      return wrapChangesAreSymmetric(as_pu2a0b_a0a0a1b0pb(myChange, NodeGroupWrapChange.class), as_pu2a0b_a1a0a1b0pb(repoChange, NodeGroupWrapChange.class));
    }
    return false;
  }

  private void buildSymmetricChangesForChildren(HierarchicalNodeGroupChange myChange, HierarchicalNodeGroupChange repoChange) {
    {
      Iterator<SNodeId> myChildId_it = ListSequence.fromList(myChange.getGroup(true).getIds()).iterator();
      Iterator<SNodeId> repoChildId_it = ListSequence.fromList(repoChange.getGroup(true).getIds()).iterator();
      SNodeId myChildId_var;
      SNodeId repoChildId_var;
      while (myChildId_it.hasNext() && repoChildId_it.hasNext()) {
        myChildId_var = myChildId_it.next();
        repoChildId_var = repoChildId_it.next();
        SNode myChild = getMyNode(myChildId_var);
        SNode repoChild = getRepoNode(repoChildId_var);
        if (myChild != null && repoChild != null) {
          addSymmetricNodes(myChild, repoChild, myChange.getGroup(true).getDependantGroupNodeIds(), repoChange.getGroup(true).getDependantGroupNodeIds());
          buildSymmetricChangesForNode(myChild, repoChild);
        }
      }
    }
  }

  private void addSymmetricNodes(SNode myNode, SNode repoNode, Set<SNodeId> myDependantIds, Set<SNodeId> repoDependantIds) {
    SNodeId myNodeId = myNode.getNodeId();
    SNodeId repoNodeId = repoNode.getNodeId();
    if (SetSequence.fromSet(myDependantIds).contains(myNodeId) || SetSequence.fromSet(repoDependantIds).contains(repoNodeId)) {
      return;
    }
    MapSequence.fromMap(mySymmetricNodeIds).put(myNodeId, repoNodeId);
    {
      Iterator<SNode> myChild_it = ListSequence.fromList(SNodeOperations.getChildren(myNode)).iterator();
      Iterator<SNode> repoChild_it = ListSequence.fromList(SNodeOperations.getChildren(repoNode)).iterator();
      SNode myChild_var;
      SNode repoChild_var;
      while (myChild_it.hasNext() && repoChild_it.hasNext()) {
        myChild_var = myChild_it.next();
        repoChild_var = repoChild_it.next();
        addSymmetricNodes(myChild_var, repoChild_var, myDependantIds, repoDependantIds);
      }
    }
  }

  @Nullable
  private static SNodeId getFirstChangedId(List<HierarchicalNodeGroupChange> changes, SNodeId nextNodeId) {
    for (HierarchicalNodeGroupChange change : changes) {
      if (!(change.getGroup(true).isEmpty())) {
        return ListSequence.fromList(change.getGroup(true).getIds()).first();
      }
    }
    return nextNodeId;
  }

  private boolean moveChangesAreSymmetric(NodeGroupMoveChange myMoveChange, NodeGroupMoveChange repoMoveChange) {
    return Objects.equals(myMoveChange.getIds(false), repoMoveChange.getIds(false));
  }

  private boolean notMoveChangesAreSymmetric(NodeGroupNotMoveChange myNotMoveChange, NodeGroupNotMoveChange repoNotMoveChange) {
    if (!(Objects.equals(myNotMoveChange.getGroup(false).getIds(), repoNotMoveChange.getGroup(false).getIds())) || ListSequence.fromList(myNotMoveChange.getGroup(true).getIds()).count() != ListSequence.fromList(repoNotMoveChange.getGroup(true).getIds()).count()) {
      return false;
    }

    {
      Iterator<SNodeId> myChildId_it = ListSequence.fromList(myNotMoveChange.getGroup(true).getIds()).iterator();
      Iterator<SNodeId> repoChildId_it = ListSequence.fromList(repoNotMoveChange.getGroup(true).getIds()).iterator();
      SNodeId myChildId_var;
      SNodeId repoChildId_var;
      while (myChildId_it.hasNext() && repoChildId_it.hasNext()) {
        myChildId_var = myChildId_it.next();
        repoChildId_var = repoChildId_it.next();
        SNode myChild = getMyNode(myChildId_var);
        SNode repoChild = getRepoNode(repoChildId_var);
        if (!(DiffUtil.nodeEquals(myChild, repoChild, myNotMoveChange.getGroup(true).getDependantGroupNodeIds(), repoNotMoveChange.getGroup(true).getDependantGroupNodeIds()))) {
          return false;
        }
      }
    }
    return true;
  }

  private boolean wrapChangesAreSymmetric(NodeGroupWrapChange myChange, NodeGroupWrapChange repoChange) {
    if (myChange.isWrap() != repoChange.isWrap()) {
      return false;
    }
    WrappingNodesGroup myWrappingGroup = myChange.getWrappingGroup();
    WrappingNodesGroup repoWrappingGroup = repoChange.getWrappingGroup();
    if (!(myChange.isWrap())) {
      return Objects.equals(myWrappingGroup.getWrappedParentId(), repoWrappingGroup.getWrappedParentId());
    }
    SNode myWrappingNode = getMyNode(myWrappingGroup.getWrappingNodeId());
    SNode repoWrappingNode = getRepoNode(repoWrappingGroup.getWrappingNodeId());
    return DiffUtil.nodeEquals(myWrappingNode, repoWrappingNode, myWrappingGroup.getDependantGroupNodeIds(), repoWrappingGroup.getDependantGroupNodeIds());
  }

  /*package*/ static Map<SContainmentLink, List<SNodeId>> getRoleToChildCollectionMap(SNode node) {
    final Map<SContainmentLink, List<SNodeId>> roleToChildCollection = new HashMap<SContainmentLink, List<SNodeId>>();
    ListSequence.fromList(SNodeOperations.getChildren(node)).visitAll((child) -> {
      SContainmentLink link = SNodeOperations.getContainingLinkInChildrenAndChildAttributesCollection(child);
      roleToChildCollection.computeIfAbsent(link, new Function<SContainmentLink, List<SNodeId>>() {
        public IListSequence<SNodeId> apply(SContainmentLink link) {
          return ListSequence.fromList(new ArrayList<SNodeId>());
        }
      }).add(child.getNodeId());
    });
    for (SContainmentLink role : CollectionSequence.fromCollection(SNodeOperations.getConcept(node).getContainmentLinks())) {
      if (!(MapSequence.fromMap(roleToChildCollection).containsKey(role))) {
        MapSequence.fromMap(roleToChildCollection).put(role, Collections.emptyList());
      }
    }
    return roleToChildCollection;
  }

  @NotNull
  @Override
  public Map<ModelChange, List<ModelChange>> getConflictingChanges() {
    return myConflictingChanges;
  }

  @NotNull
  @Override
  public Map<ModelChange, List<ModelChange>> getSymmetricChanges() {
    return mySymmetricChanges;
  }

  @NotNull
  @Override
  public ChangeSet getMyChangeSet() {
    return myMineChangeSet;
  }

  @NotNull
  @Override
  public ChangeSet getRepositoryChangeSet() {
    return myRepositoryChangeSet;
  }

  private void addPossibleConflict(ModelChange a, ModelChange b) {
    if (a.isNonConflicting() || b.isNonConflicting()) {
      addSymmetric(a, b);
    } else {
      addConflict(a, b);
    }
  }

  private void addConflict(ModelChange a, ModelChange b) {
    addChangeLink(myConflictingChanges, a, b);
  }

  private void addSymmetric(ModelChange a, ModelChange b) {
    addChangeLink(mySymmetricChanges, a, b);
  }

  private void collectConflicts() {
    collectSymmetricHierarchicalChanges();
    for (ModelChange myChange : ListSequence.fromList(myMineChangeSet.getModelChanges())) {
      for (ModelChange repoChange : ListSequence.fromList(myRepositoryChangeSet.getModelChanges())) {
        collectConflicts(myChange, repoChange);
      }
    }
  }

  private void collectConflicts(ModelChange myChange, ModelChange repoChange) {
    if (myChange instanceof HierarchicalNodeGroupChange && repoChange instanceof HierarchicalNodeGroupChange) {
      collectHierarchicalConflicts(as_pu2a0b_a0a0a0a37(myChange, HierarchicalNodeGroupChange.class), as_pu2a0b_a1a0a0a37(repoChange, HierarchicalNodeGroupChange.class));
      return;
    }
    if (!(myChange.conflictsWith(repoChange))) {
      return;
    }
    if (myChange.isSymmetricWith(repoChange)) {
      addSymmetric(myChange, repoChange);
    } else {
      addPossibleConflict(myChange, repoChange);
    }
  }

  /*package*/ void collectHierarchicalConflicts(HierarchicalNodeGroupChange myChange, HierarchicalNodeGroupChange repoChange) {
    if (!(MapSequence.fromMap(mySymmetricChanges).containsKey(myChange)) && !(MapSequence.fromMap(mySymmetricChanges).containsKey(repoChange))) {
      if (myChange.conflictsWith(repoChange, mySymmetricNodeIds, myWrapConflictsWithInternalChanges)) {
        addPossibleConflict(myChange, repoChange);
      }
    }
  }

  private static void addOneWayChangeLink(Map<ModelChange, List<ModelChange>> map, ModelChange a, ModelChange b) {
    if (MapSequence.fromMap(map).get(a) == null) {
      MapSequence.fromMap(map).put(a, ListSequence.fromList(new ArrayList<ModelChange>()));
    }
    ListSequence.fromList(MapSequence.fromMap(map).get(a)).addElement(b);
  }

  private static void addChangeLink(Map<ModelChange, List<ModelChange>> map, ModelChange a, ModelChange b) {
    assert a.getChangeSet() != b.getChangeSet();
    addOneWayChangeLink(map, a, b);
    addOneWayChangeLink(map, b, a);
  }
  private static <T> T as_pu2a0b_a0a0a1a14(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_pu2a0b_a1a0a1a14(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_pu2a0b_a0a0a0b0pb(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_pu2a0b_a1a0a0b0pb(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_pu2a0b_a0a0a1b0pb(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_pu2a0b_a1a0a1b0pb(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_pu2a0b_a0a0a0a37(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_pu2a0b_a1a0a0a37(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
}
