package com.nitorcreations.willow.utils;

import static java.nio.file.StandardWatchEventKinds.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Map.Entry;

public class FilePollingProperties {
  public interface PropertyChangeListerner {
    public void propertyValueChanged(String key, String newValue, String oldValue);
    public void propertyAdded(String key, String value);
    public void propertyRemoved(String key, String value);
  }
  private final MergeableProperties properties;
  
  public FilePollingProperties(final File source, final PropertyChangeListerner listener) throws IOException {
    properties = new MergeableProperties();
    properties.load(new FileInputStream(source));
    final Path sourcePath = source.getAbsoluteFile().getParentFile().toPath();
    try {
      final WatchService watcher = FileSystems.getDefault().newWatchService();
      sourcePath.register(watcher, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE);
      new Thread(new Runnable() {
        @Override
        public void run() {
          for (;;) {
            WatchKey key = null;
            try {
              key = watcher.take();
            } catch (InterruptedException x) {
              return;
            }
            for (WatchEvent<?> event: key.pollEvents()) {
              WatchEvent.Kind<?> kind = event.kind();
              if (kind == OVERFLOW) {
                  continue;
              }
              @SuppressWarnings("unchecked")
              WatchEvent<Path> ev = (WatchEvent<Path>)event;
              Path context = ev.context();
              if (!context.equals(source.toPath())) {
                continue;
              }
              if (kind == ENTRY_DELETE) {
                for (Entry<String, String> next : properties.backingEntrySet()) {
                  listener.propertyRemoved(next.getKey(), next.getValue());
                }
                properties.clear();
              } else {
                MergeableProperties newProps = new MergeableProperties();
                try {
                  newProps.load(new FileInputStream(source));
                  MergeableProperties oldProps ;
                  synchronized (properties) {
                    oldProps = (MergeableProperties) properties.clone();
                    properties.clear();
                    properties.putAll(newProps);
                  }
                  for (Entry<String, String> next : newProps.backingEntrySet()) {
                      if (oldProps.containsKey(next.getKey())) {
                        if (!oldProps.getProperty(next.getKey()).equals(next.getValue())) {
                          listener.propertyValueChanged(next.getKey(), next.getValue(), oldProps.getProperty(next.getKey()));
                        }
                        oldProps.remove(next.getKey());
                      } else {
                        listener.propertyAdded(next.getKey(), next.getValue());
                      }
                    }
                    for (Entry<String, String> next : oldProps.backingEntrySet()) {
                      listener.propertyRemoved(next.getKey(), next.getValue());
                    }
                } catch (IOException e) {
                }
              }
            }
            boolean valid = key.reset();
            if (!valid) {
              break;
            }
            continue;
          }
        }
      }).start();
    } catch (Exception e) {
    }
  }
  public MergeableProperties getProperties() {
    synchronized (properties) {
      return (MergeableProperties) properties.clone();
    }
  }
  public static void main(String[] args) throws IOException {
    new FilePollingProperties(new File(args[0]), new PropertyChangeListerner() {
      @Override
      public void propertyValueChanged(String key, String newValue, String oldValue) {
        System.out.println("CHANGE: " + key + " = " + oldValue + " => " + newValue);
      }

      @Override
      public void propertyRemoved(String key, String value) {
        System.out.println("REMOVED: " + key + " = " + value);
      }

      @Override
      public void propertyAdded(String key, String value) {
        System.out.println("ADDED: " + key + " = " + value);
      }
    });
  }
}

@@ -144,16 +144,54 @@
 Stream;%0A
+import java.io.FileNotFoundException;%0A
 import j
@@ -397,16 +397,81 @@
 p.Entry;
+%0Aimport java.util.logging.Level;%0Aimport java.util.logging.Logger;
 %0A%0Apublic
@@ -750,16 +750,780 @@
 e);%0A  %7D%0A
+  public static class LoggingPropertyChangeListener implements PropertyChangeListerner %7B%0A    private final Logger log;%0A    private final Level level;%0A    public LoggingPropertyChangeListener(String loggerName, Level level) %7B%0A      this.log = Logger.getLogger(loggerName);%0A      this.level = level;%0A    %7D%0A    @Override%0A    public void propertyValueChanged(String key, String newValue, String oldValue) %7B%0A      log.log(level, %22CHANGE: %22 + key + %22 = %22 + oldValue + %22 =%3E %22 + newValue);%0A    %7D%0A%0A    @Override%0A    public void propertyRemoved(String key, String value) %7B%0A      log.log(level, %22REMOVED: %22 + key + %22 = %22 + value);%0A    %7D%0A%0A    @Override%0A    public void propertyAdded(String key, String value) %7B%0A      log.log(level, %22ADDED: %22 + key + %22 = %22 + value);%0A    %7D%0A  %7D%0A
   privat
@@ -1562,18 +1562,16 @@
 erties;%0A
-  
 %0A  publi
@@ -1611,16 +1611,176 @@
 e source
+) %7B%0A    this(source, new LoggingPropertyChangeListener(FilePollingProperties.class.getName(), Level.FINE));%0A  %7D%0A  public FilePollingProperties(final File source
 , final 
@@ -1812,35 +1812,16 @@
 istener)
- throws IOException
  %7B%0A    p
@@ -1855,24 +1855,36 @@
 operties();%0A
+    try %7B%0A  
     properti
@@ -1917,24 +1917,73 @@
 m(source));%0A
+    %7D catch (IOException e) %7B%0A      //Noop%0A    %7D%0A
     final Pa
@@ -4983,533 +4983,57 @@
 new 
-PropertyChangeListerner() %7B%0A      @Override%0A      public void p
+LoggingP
 roperty
-Value
 Change
-d(String key, String newValue, String oldValue) %7B%0A        System.out.println(%22CHANGE: %22 + key + %22 = %22 + oldValue + %22 =%3E %22 + newValue);%0A      %7D%0A%0A      @Override%0A      public void propertyRemoved(String key, String value) %7B%0A        System.out.println(%22REMOVED: %22 + key + %22 = %22 + value);%0A      %7D%0A%0A      @Override%0A      public void propertyAdded(String key, String value) %7B%0A        System.out.println(%22ADDED: %22 + key + %22 = %22 + value);%0A      %7D%0A    %7D
+Listener(%22main%22, Level.INFO)
 );%0A 

Make safer