@Plugin(
      name = "Add End Artificial Events",
      parameterLabels = {"Log"},
      returnLabels = {"Altered log"},
      returnTypes = {XLog.class},
      userAccessible = true,
      help = "Adds an artificial  end task to every trace in the log file")
  @UITopiaVariant(
      affiliation = "Department of Computer Science University of Pisa",
      author = "R.Guanciale,G.Spagnolo et al.",
      email = "*****@*****.**",
      pack = "PetriNetReplayAnalysis")
  public XLog addEvents(PluginContext context, XLog oldLog) {

    context.getFutureResult(0).setLabel(XConceptExtension.instance().extractName(oldLog));

    context.getProgress().setMinimum(0);
    context.getProgress().setMaximum(oldLog.size());
    context.getProgress().setIndeterminate(false);
    context.getProgress().setValue(0);

    XAttributeMap logattlist = copyAttMap(oldLog.getAttributes());
    XLog newLog = new XLogImpl(logattlist);
    for (int i = 0; i < oldLog.size(); i++) {
      XTrace oldTrace = oldLog.get(i);
      XTrace newTrace = new XTraceImpl(copyAttMap(oldTrace.getAttributes()));
      for (int j = 0; j < oldTrace.size(); j++) {
        XEvent oldEvent = oldTrace.get(j);
        XEvent newEvent = new XEventImpl(copyAttMap(oldEvent.getAttributes()));
        newTrace.add(newEvent);
      }

      Date time = new Date();
      try {
        time =
            ((XAttributeTimestampImpl)
                    oldTrace.get(oldTrace.size() - 1).getAttributes().get("time:timestamp"))
                .getValue();
        time.setTime(time.getTime() + 1);
      } catch (Exception ex) {
      }

      newTrace.add(makeEvent("ArtificialEnd", time));

      newLog.add(newTrace);
      context.getProgress().inc();
    }
    return newLog;
  }
  private void updatePerformance(
      Petrinet net,
      Marking initMarking,
      List<Transition> sequence,
      PetrinetSemantics semantics,
      XTrace trace,
      TotalPerformanceResult totalResult,
      Map<Transition, XEventClass> map,
      String tracename) {
    // if (trace.size() != sequence.size())
    //     System.exit(1);

    XAttributeTimestampImpl date =
        (XAttributeTimestampImpl) (trace.get(0).getAttributes().get("time:timestamp"));
    long d1 = date.getValue().getTime();

    Map<Place, PerformanceData> performance = new HashMap<Place, PerformanceData>();

    Map<Arc, Integer> maparc = new HashMap<Arc, Integer>();

    Marking marking = new Marking(initMarking);

    for (Place place : marking) {
      PerformanceData result = null;
      if (performance.containsKey(place)) result = performance.get(place);
      else result = new PerformanceData();

      result.addToken();

      performance.put(place, result);
    }

    int iTrace = -1;
    for (int iTrans = 0; iTrans < sequence.size(); iTrans++) {
      Transition transition = sequence.get(iTrans);
      long d2 = d1;
      if (map.containsKey(transition)) {
        iTrace += 1;
      }
      if (iTrace >= 0) {
        XEvent event = trace.get(iTrace);
        XAttributeTimestampImpl date1 =
            (XAttributeTimestampImpl) (event.getAttributes().get("time:timestamp"));
        d2 = date1.getValue().getTime();
      }
      float deltaTime = d2 - d1;
      d1 = d2;

      // boolean fittingTransition = true;
      Collection<PetrinetEdge<? extends PetrinetNode, ? extends PetrinetNode>> preset =
          net.getInEdges(transition);

      Set<Place> places = new HashSet<Place>();
      places.addAll(marking);
      List<Transition> futureTrans = sequence.subList(iTrans, sequence.size());
      for (Place place : places) {
        PerformanceData result = null;
        if (performance.containsKey(place)) result = performance.get(place);
        else result = new PerformanceData();

        int placeMarking = marking.occurrences(place);
        if (placeMarking == 0) continue;

        // Transitions denending on the current place
        int maxMarking = 0;
        int minTransitionDistanceInFuture = futureTrans.size();
        for (PetrinetEdge<? extends PetrinetNode, ? extends PetrinetNode> edge :
            net.getOutEdges(place)) {
          if (!(edge instanceof Arc)) continue;
          Arc arc = (Arc) edge;

          Transition trs = (Transition) arc.getTarget();
          int trsPos = futureTrans.indexOf(trs);
          if (trsPos < 0) continue;
          if (trsPos > minTransitionDistanceInFuture) continue;
          minTransitionDistanceInFuture = trsPos;

          // Transition preset
          int minMarking = placeMarking;
          for (PetrinetEdge<? extends PetrinetNode, ? extends PetrinetNode> edge1 :
              net.getInEdges(trs)) {
            if (!(edge1 instanceof Arc)) continue;
            Arc arc1 = (Arc) edge1;

            Place p1 = (Place) arc1.getSource();
            int tokens = marking.occurrences(p1);
            minMarking = Math.min(minMarking, tokens);
          }
          // maxMarking = Math.max(maxMarking, minMarking);
          maxMarking = minMarking;
        }
        // maxMarking < placeMarking
        // maxMarking is the consumable tokens
        // synchTime = (placeMarking - maxMarking) *  deltaTime;
        result.addTime(placeMarking * deltaTime, maxMarking * deltaTime);
        performance.put(place, result);
      }

      // Updates marking according with enabled transition
      for (PetrinetEdge<? extends PetrinetNode, ? extends PetrinetNode> edge : preset) {
        if (edge instanceof Arc) {
          Arc arc = (Arc) edge;
          // add arc usage
          addArcUsage(arc, maparc);
          Place place = (Place) arc.getSource();
          int consumed = arc.getWeight();
          int missing = 0;
          if (arc.getWeight() > marking.occurrences(place)) {
            missing = arc.getWeight() - marking.occurrences(place);
          }
          for (int i = missing; i < consumed; i++) {
            marking.remove(place);
          }
        }
      }
      Collection<PetrinetEdge<? extends PetrinetNode, ? extends PetrinetNode>> postset =
          net.getOutEdges(transition);
      for (PetrinetEdge<? extends PetrinetNode, ? extends PetrinetNode> edge : postset) {
        if (edge instanceof Arc) {
          Arc arc = (Arc) edge;
          // add arc usage
          addArcUsage(arc, maparc);
          Place place = (Place) arc.getTarget();
          int produced = arc.getWeight();
          for (int i = 0; i < produced; i++) {
            marking.add(place);

            PerformanceData result = null;
            if (performance.containsKey(place)) result = performance.get(place);
            else result = new PerformanceData();
            result.addToken();
            performance.put(place, result);
          }
        }
      }
    }
    PerformanceResult pr = new PerformanceResult(tracename);
    pr.setList(performance);
    pr.setMaparc(maparc);
    totalResult.getListperformance().add(pr);
  }