001/* *********************************************************************** * 002 * project: org.matsim.* 003 * * 004 * *********************************************************************** * 005 * * 006 * copyright : (C) 2010 by the members listed in the COPYING, * 007 * LICENSE and WARRANTY file. * 008 * email : info at matsim dot org * 009 * * 010 * *********************************************************************** * 011 * * 012 * This program is free software; you can redistribute it and/or modify * 013 * it under the terms of the GNU General Public License as published by * 014 * the Free Software Foundation; either version 2 of the License, or * 015 * (at your option) any later version. * 016 * See also COPYING, LICENSE and WARRANTY file * 017 * * 018 * *********************************************************************** */ 019 020package org.matsim.utils.eventsfilecomparison; 021 022import java.util.Map; 023import java.util.Map.Entry; 024import java.util.concurrent.CyclicBarrier; 025 026import org.apache.log4j.Logger; 027import org.matsim.api.core.v01.events.Event; 028 029/** 030 * This class checks if two events files are semantic equivalent. The order of the events does not matter as long as 031 * they are chronologically sorted. 032 * 033 * @author mrieser 034 * @author laemmel 035 */ 036public final class EventsFileComparator { 037 private static final Logger log = Logger.getLogger(EventsFileComparator.class); 038 039 @Deprecated // use Result enum 040 public static final int CODE_FILES_ARE_EQUAL = 0; 041 @Deprecated // use Result enum 042 public static final int CODE_DIFFERENT_NUMBER_OF_TIMESTEPS = -1; 043 @Deprecated // use Result enum 044 public static final int CODE_DIFFERENT_TIMESTEPS = -2; 045 @Deprecated // use Result enum 046 public static final int CODE_MISSING_EVENT = -3; 047 @Deprecated // use Result enum 048 public static final int CODE_WRONG_EVENT_COUNT = -4; 049 050 public enum Result { FILES_ARE_EQUAL, DIFFERENT_NUMBER_OF_TIMESTEPS, 051 DIFFERENT_TIMESTEPS, MISSING_EVENT, WRONG_EVENT_COUNT } 052 053 private boolean ignoringCoordinates = false; 054 public EventsFileComparator setIgnoringCoordinates( boolean ignoringCoordinates ){ 055 this.ignoringCoordinates = ignoringCoordinates; 056 return this; 057 } 058 059 public static void main(String[] args) { 060 if (args.length != 2) { 061 System.out.println("Error: expected 2 events files as input arguments but found " + args.length); 062 System.out.println("Syntax: EventsFileComparator eventsFile1 eventsFile2"); 063 } else { 064 String filename1 = args[0]; 065 String filename2 = args[1]; 066 067 EventsFileComparator.compare(filename1, filename2); 068 } 069 } 070 071 072 /** 073 * Compares two Events files. This method is thread-safe. 074 * 075 * @param filename1 076 * @param filename2 077 * @return <code>0</code> if the events files are equal, or some error code (see constants) if not. 078 */ 079 @Deprecated // prefer the variant returning a Result enum. kai, nov'17 080 public static int compareAndReturnInt(final String filename1, final String filename2) { 081 Result result = compare( filename1, filename2 ) ; 082 switch ( result ) { 083 case DIFFERENT_NUMBER_OF_TIMESTEPS: 084 return -1 ; 085 case DIFFERENT_TIMESTEPS: 086 return -2 ; 087 case FILES_ARE_EQUAL: 088 return 0 ; 089 case MISSING_EVENT: 090 return -3 ; 091 case WRONG_EVENT_COUNT: 092 return -4 ; 093 default: 094 throw new RuntimeException("unknown Result code") ; 095 } 096 } 097 public Result runComparison( final String filename1, final String filename2 ) { 098 // (need method name different from pre-existing static method. kai, feb'20) 099 100 EventsComparator comparator = new EventsComparator( ); 101 CyclicBarrier doComparison = new CyclicBarrier(2, comparator); 102 Worker w1 = new Worker(filename1, doComparison, ignoringCoordinates ); 103 Worker w2 = new Worker(filename2, doComparison, ignoringCoordinates ); 104 comparator.setWorkers(w1, w2); 105 w1.start(); 106 w2.start(); 107 108 try { 109 w1.join(); 110 } catch (InterruptedException e) { 111 e.printStackTrace(); 112 } 113 try { 114 w2.join(); 115 } catch (InterruptedException e) { 116 e.printStackTrace(); 117 } 118 119 Result retCode = comparator.retCode; 120 if (retCode == Result.FILES_ARE_EQUAL) { 121 log.info("Event files are semantic equivalent."); 122 } else { 123 log.warn("Event files differ."); 124 } 125 return retCode; 126 } 127 public static Result compare(final String filename1, final String filename2) { 128 return new EventsFileComparator().runComparison( filename1, filename2 ); 129 } 130 131 private static class EventsComparator implements Runnable { 132 133 private Worker worker1 = null; 134 private Worker worker2 = null; 135 private volatile Result retCode = null ; 136 137 /*package*/ void setWorkers(final Worker w1, final Worker w2) { 138 this.worker1 = w1; 139 this.worker2 = w2; 140 } 141 142 @Override 143 public void run() { 144 if (this.worker1.getCurrentTime() != this.worker2.getCurrentTime()) { 145 log.warn("Differnt time steps in event files!"); 146 setExitCode(Result.DIFFERENT_TIMESTEPS); 147 return; 148 } 149 150 if (this.worker1.isFinished() != this.worker2.isFinished()) { 151 log.warn("Events files have different number of time steps!"); 152 setExitCode(Result.DIFFERENT_NUMBER_OF_TIMESTEPS); 153 return; 154 } 155 156 Map<String, Counter> map1 = this.worker1.getEventsMap(); 157 Map<String, Counter> map2 = this.worker2.getEventsMap(); 158 159 boolean problem = false ; 160 161 // check that map2 contains all keys of map1, with the same values 162 for (Entry<String, Counter> entry : map1.entrySet()) { 163 164 Counter counter = map2.get(entry.getKey()); 165 if (counter == null) { 166 log.warn("The event:" ) ; 167 log.warn( entry.getKey() ); 168 log.warn("is missing in events file:" + worker2.getEventsFile()); 169 setExitCode(Result.MISSING_EVENT); 170 problem = true ; 171 } else{ 172 if( counter.getCount() != entry.getValue().getCount() ){ 173 log.warn( 174 "Wrong event count for: " + entry.getKey() + "\n" + entry.getValue().getCount() + " times in file:" + worker1.getEventsFile() 175 + "\n" + counter.getCount() + " times in file:" + worker2.getEventsFile() ); 176 setExitCode( Result.WRONG_EVENT_COUNT ); 177 problem = true; 178 } 179 } 180 } 181 182 // also check that map1 contains all keys of map2 183 for (Entry<String, Counter> e : map2.entrySet()) { 184 Counter counter = map1.get(e.getKey()); 185 if (counter == null) { 186 log.warn("The event:"); 187 log.warn(e.getKey()); 188 log.warn("is missing in events file:" + worker1.getEventsFile()); 189 setExitCode(Result.MISSING_EVENT); 190 problem = true ; 191 } 192 } 193 194 if ( problem ) { 195 return ; 196 } 197 198 if (this.worker1.isFinished()) { 199 setExitCode(Result.FILES_ARE_EQUAL); 200 } 201 } 202 203 private void setExitCode(final Result errCode) { 204 this.retCode= errCode; 205 if (errCode != Result.FILES_ARE_EQUAL) { 206 this.worker1.interrupt(); 207 this.worker2.interrupt(); 208 } 209 } 210 } 211 212}