/**
   * Populates the conflict resolver with one tag collection
   *
   * @param tagsForAllPrimitives the tag collection
   * @param sourceStatistics histogram of tag source, number of primitives of each type in the
   *     source
   * @param targetStatistics histogram of paste targets, number of primitives of each type in the
   *     paste target
   */
  public void populate(
      TagCollection tagsForAllPrimitives,
      Map<OsmPrimitiveType, Integer> sourceStatistics,
      Map<OsmPrimitiveType, Integer> targetStatistics) {
    mode = Mode.RESOLVING_ONE_TAGCOLLECTION_ONLY;
    tagsForAllPrimitives =
        tagsForAllPrimitives == null ? new TagCollection() : tagsForAllPrimitives;
    sourceStatistics = sourceStatistics == null ? new HashMap<>() : sourceStatistics;
    targetStatistics = targetStatistics == null ? new HashMap<>() : targetStatistics;

    // init the resolver
    //
    allPrimitivesResolver
        .getModel()
        .populate(tagsForAllPrimitives, tagsForAllPrimitives.getKeysWithMultipleValues());
    allPrimitivesResolver.getModel().prepareDefaultTagDecisions();

    // prepare the dialog with one tag resolver
    pnlTagResolver.removeAll();
    pnlTagResolver.add(allPrimitivesResolver, BorderLayout.CENTER);

    statisticsModel.reset();
    StatisticsInfo info = new StatisticsInfo();
    info.numTags = tagsForAllPrimitives.getKeys().size();
    info.sourceInfo.putAll(sourceStatistics);
    info.targetInfo.putAll(targetStatistics);
    statisticsModel.append(info);
    validate();
  }
 /**
  * Initializes the conflict resolver for a specific type of primitives
  *
  * @param type the type of primitives
  * @param tc the tags belonging to this type of primitives
  * @param targetStatistics histogram of paste targets, number of primitives of each type in the
  *     paste target
  */
 protected void initResolver(
     OsmPrimitiveType type, TagCollection tc, Map<OsmPrimitiveType, Integer> targetStatistics) {
   resolvers.get(type).getModel().populate(tc, tc.getKeysWithMultipleValues());
   resolvers.get(type).getModel().prepareDefaultTagDecisions();
   if (!tc.isEmpty() && targetStatistics.get(type) != null && targetStatistics.get(type) > 0) {
     tpResolvers.add(PANE_TITLES.get(type), resolvers.get(type));
   }
 }
  /**
   * Inform a non-expert user about what tag conflict resolution means.
   *
   * @param primitives The primitives to be combined
   * @param normalizedTags The normalized tag collection of the primitives to be combined
   * @throws UserCancelException If the user cancels the dialog.
   */
  protected static void informAboutTagConflicts(
      final Collection<? extends OsmPrimitive> primitives, final TagCollection normalizedTags)
      throws UserCancelException {
    String conflicts =
        Utils.joinAsHtmlUnorderedList(
            Utils.transform(
                normalizedTags.getKeysWithMultipleValues(),
                new Function<String, String>() {
                  @Override
                  public String apply(String key) {
                    return tr(
                        "{0} ({1})",
                        key,
                        Utils.join(
                            tr(", "),
                            Utils.transform(
                                normalizedTags.getValues(key),
                                new Function<String, String>() {
                                  @Override
                                  public String apply(String x) {
                                    return x == null || x.isEmpty() ? tr("<i>missing</i>") : x;
                                  }
                                })));
                  }
                }));
    String msg = /* for correct i18n of plural forms - see #9110 */
        trn(
            "You are about to combine {0} objects, "
                + "but the following tags are used conflictingly:<br/>{1}"
                + "If these objects are combined, the resulting object may have unwanted tags.<br/>"
                + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>"
                + "Do you want to continue?",
            "You are about to combine {0} objects, "
                + "but the following tags are used conflictingly:<br/>{1}"
                + "If these objects are combined, the resulting object may have unwanted tags.<br/>"
                + "If you want to continue, you are shown a dialog to fix the conflicting tags.<br/><br/>"
                + "Do you want to continue?",
            primitives.size(),
            primitives.size(),
            conflicts);

    if (!ConditionalOptionPaneUtil.showConfirmationDialog(
        "combine_tags",
        Main.parent,
        "<html>" + msg + "</html>",
        tr("Combine confirmation"),
        JOptionPane.YES_NO_OPTION,
        JOptionPane.QUESTION_MESSAGE,
        JOptionPane.YES_OPTION)) {
      throw new UserCancelException();
    }
  }
  /**
   * Replies the list of {@link Command commands} needed to resolve specified conflicts, by
   * displaying if necessary a {@link CombinePrimitiveResolverDialog} to the user. This dialog will
   * allow the user to choose conflict resolution actions.
   *
   * <p>Non-expert users are informed first of the meaning of these operations, allowing them to
   * cancel.
   *
   * @param tagsOfPrimitives The tag collection of the primitives to be combined. Should generally
   *     be equal to {@code TagCollection.unionOfAllPrimitives(primitives)}
   * @param primitives The primitives to be combined
   * @param targetPrimitives The primitives the collection of primitives are merged or combined to.
   * @return The list of {@link Command commands} needed to apply resolution actions.
   * @throws UserCancelException If the user cancelled a dialog.
   */
  public static List<Command> launchIfNecessary(
      final TagCollection tagsOfPrimitives,
      final Collection<? extends OsmPrimitive> primitives,
      final Collection<? extends OsmPrimitive> targetPrimitives)
      throws UserCancelException {

    CheckParameterUtil.ensureParameterNotNull(tagsOfPrimitives, "tagsOfPrimitives");
    CheckParameterUtil.ensureParameterNotNull(primitives, "primitives");
    CheckParameterUtil.ensureParameterNotNull(targetPrimitives, "targetPrimitives");

    final TagCollection completeWayTags = new TagCollection(tagsOfPrimitives);
    TagConflictResolutionUtil.combineTigerTags(completeWayTags);
    TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(completeWayTags, primitives);
    final TagCollection tagsToEdit = new TagCollection(completeWayTags);
    TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit);

    final Set<Relation> parentRelations = OsmPrimitive.getParentRelations(primitives);

    // Show information dialogs about conflicts to non-experts
    if (!ExpertToggleAction.isExpert()) {
      // Tag conflicts
      if (!completeWayTags.isApplicableToPrimitive()) {
        informAboutTagConflicts(primitives, completeWayTags);
      }
      // Relation membership conflicts
      if (!parentRelations.isEmpty()) {
        informAboutRelationMembershipConflicts(primitives, parentRelations);
      }
    }

    List<Command> cmds = new LinkedList<>();

    if (!GraphicsEnvironment.isHeadless()) {
      // Build conflict resolution dialog
      final CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();

      dialog
          .getTagConflictResolverModel()
          .populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues());
      dialog.getRelationMemberConflictResolverModel().populate(parentRelations, primitives);
      dialog.prepareDefaultDecisions();

      // Ensure a proper title is displayed instead of a previous target (fix #7925)
      if (targetPrimitives.size() == 1) {
        dialog.setTargetPrimitive(targetPrimitives.iterator().next());
      } else {
        dialog.setTargetPrimitive(null);
      }

      // Resolve tag conflicts if necessary
      if (!dialog.isResolvedCompletely()) {
        dialog.setVisible(true);
        if (!dialog.isApplied()) {
          throw new UserCancelException();
        }
      }
      for (OsmPrimitive i : targetPrimitives) {
        dialog.setTargetPrimitive(i);
        cmds.addAll(dialog.buildResolutionCommands());
      }
    }
    return cmds;
  }