2828
2929import static engineering .swat .watch .impl .mac .apis .FileSystemEvents .FSEventStreamCreateFlag .FILE_EVENTS ;
3030import static engineering .swat .watch .impl .mac .apis .FileSystemEvents .FSEventStreamCreateFlag .NO_DEFER ;
31+ import static engineering .swat .watch .impl .mac .apis .FileSystemEvents .FSEventStreamCreateFlag .USE_CF_TYPES ;
32+ import static engineering .swat .watch .impl .mac .apis .FileSystemEvents .FSEventStreamCreateFlag .USE_EXTENDED_DATA ;
3133import static engineering .swat .watch .impl .mac .apis .FileSystemEvents .FSEventStreamCreateFlag .WATCH_ROOT ;
3234import static org .awaitility .Awaitility .await ;
3335
5254import com .sun .jna .Pointer ;
5355import com .sun .jna .platform .mac .CoreFoundation ;
5456import com .sun .jna .platform .mac .CoreFoundation .CFArrayRef ;
57+ import com .sun .jna .platform .mac .CoreFoundation .CFDictionaryRef ;
5558import com .sun .jna .platform .mac .CoreFoundation .CFIndex ;
59+ import com .sun .jna .platform .mac .CoreFoundation .CFNumberRef ;
5660import com .sun .jna .platform .mac .CoreFoundation .CFStringRef ;
5761
5862import engineering .swat .watch .TestDirectory ;
@@ -78,13 +82,13 @@ void smokeTest() throws IOException {
7882 var paths = ConcurrentHashMap .<String > newKeySet ();
7983
8084 var s = test .getTestDirectory ().toString ();
81- var handler = (MinimalWorkingExample .EventHandler ) (path , flags , id ) -> {
85+ var handler = (MinimalWorkingExample .EventHandler ) (path , inode , flags , id ) -> {
8286 synchronized (ready ) {
8387 while (!ready .get ()) {
8488 try {
8589 ready .wait ();
8690 } catch (InterruptedException e ) {
87- LOGGER .error ("Unexpected interrupt. Test likely to fail. Event ignored ({})." , prettyPrint (path , flags , id ));
91+ LOGGER .error ("Unexpected interrupt. Test likely to fail. Event ignored ({})." , prettyPrint (path , inode , flags , id ));
8892 Thread .currentThread ().interrupt ();
8993 return ;
9094 }
@@ -93,7 +97,7 @@ void smokeTest() throws IOException {
9397 paths .remove (path );
9498 };
9599
96- try (var mwe = new MinimalWorkingExample (s , handler )) {
100+ try (var mwe = new MinimalWorkingExample (s , handler , true )) {
97101 var dir = test .getTestDirectory ().toRealPath ();
98102 paths .add (Files .writeString (dir .resolve ("a.txt" ), "foo" ).toString ());
99103 paths .add (Files .writeString (dir .resolve ("b.txt" ), "bar" ).toString ());
@@ -111,33 +115,34 @@ void smokeTest() throws IOException {
111115
112116 public static void main (String [] args ) throws IOException {
113117 var s = args [0 ];
114- var handler = (MinimalWorkingExample .EventHandler ) (path , flags , id ) -> {
115- LOGGER .info (prettyPrint (path , flags , id ));
118+ var handler = (MinimalWorkingExample .EventHandler ) (path , inode , flags , id ) -> {
119+ LOGGER .info (prettyPrint (path , inode , flags , id ));
116120 };
121+ var useExtendedData = args .length >= 2 && Boolean .parseBoolean (args [1 ]);
117122
118- try (var mwe = new MinimalWorkingExample (s , handler )) {
123+ try (var mwe = new MinimalWorkingExample (s , handler , useExtendedData )) {
119124 // Block the program from terminating until `ENTER` is pressed
120125 new BufferedReader (new InputStreamReader (System .in )).readLine ();
121126 }
122127 }
123128
124- private static String prettyPrint (String path , int flags , long id ) {
129+ private static String prettyPrint (String path , long inode , int flags , long id ) {
125130 var flagsPrettyPrinted = Stream
126131 .of (FSEventStreamEventFlag .values ())
127132 .filter (f -> (f .mask & flags ) == f .mask )
128133 .map (Object ::toString )
129134 .collect (Collectors .joining (", " ));
130135
131- var format = "path: \" %s\" , flags: [%s], id: %s" ;
132- return String .format (format , path , flagsPrettyPrinted , id );
136+ var format = "path: \" %s\" , inode: %s, flags: [%s], id: %s" ;
137+ return String .format (format , path , inode , flagsPrettyPrinted , id );
133138 }
134139
135140 private static class MinimalWorkingExample implements Closeable {
136141 private FileSystemEvents .FSEventStreamCallback callback ;
137142 private Pointer stream ;
138143 private Pointer queue ;
139144
140- public MinimalWorkingExample (String s , EventHandler handler ) {
145+ public MinimalWorkingExample (String s , EventHandler handler , boolean useExtendedData ) {
141146
142147 // Allocate singleton array of paths
143148 CFStringRef pathToWatch = CFStringRef .createCFString (s );
@@ -154,11 +159,24 @@ public MinimalWorkingExample(String s, EventHandler handler) {
154159
155160 // Allocate callback
156161 this .callback = (x1 , x2 , x3 , x4 , x5 , x6 ) -> {
157- var paths = x4 .getStringArray (0 , (int ) x3 );
158- var flags = x5 .getIntArray (0 , (int ) x3 );
159- var ids = x6 .getLongArray (0 , (int ) x3 );
162+ var paths = x4 .getStringArray (0 , (int ) x3 );
163+ var inodes = new long [(int ) x3 ];
164+ var flags = x5 .getIntArray (0 , (int ) x3 );
165+ var ids = x6 .getLongArray (0 , (int ) x3 );
166+
167+ if (useExtendedData ) {
168+ var extendedData = new CFArrayRef (x4 );
169+ for (int i = 0 ; i < x3 ; i ++) {
170+ var dictionary = new CFDictionaryRef (extendedData .getValueAtIndex (i ));
171+ var dictionaryPath = dictionary .getValue (FileSystemEvents .kFSEventStreamEventExtendedDataPathKey );
172+ var dictionaryInode = dictionary .getValue (FileSystemEvents .kFSEventStreamEventExtendedFileIDKey );
173+ paths [i ] = dictionaryPath == null ? null : new CFStringRef (dictionaryPath ).stringValue ();
174+ inodes [i ] = dictionaryInode == null ? 0 : new CFNumberRef (dictionaryInode ).longValue ();
175+ }
176+ }
177+
160178 for (int i = 0 ; i < x3 ; i ++) {
161- handler .handle (paths [i ], flags [i ], ids [i ]);
179+ handler .handle (paths [i ], inodes [ i ], flags [i ], ids [i ]);
162180 }
163181 };
164182
@@ -170,7 +188,8 @@ public MinimalWorkingExample(String s, EventHandler handler) {
170188 pathsToWatch ,
171189 FSE .FSEventsGetCurrentEventId (),
172190 0.15 ,
173- NO_DEFER .mask | WATCH_ROOT .mask | FILE_EVENTS .mask );
191+ NO_DEFER .mask | WATCH_ROOT .mask | FILE_EVENTS .mask |
192+ (useExtendedData ? USE_EXTENDED_DATA .mask | USE_CF_TYPES .mask : 0 ));
174193
175194 // Deallocate array of paths
176195 pathsToWatch .release ();
@@ -203,7 +222,7 @@ public void close() throws IOException {
203222
204223 @ FunctionalInterface
205224 private static interface EventHandler {
206- void handle (String path , int flags , long id );
225+ void handle (String path , long inode , int flags , long id );
207226 }
208227 }
209228}
0 commit comments