feature[TW19378]: BAT - Implement new refreshing methods for tool

Change-Id: Id0181c4bfe928764d99d8784642730b0b3b94ba8
Signed-off-by: Branden Phillips <branden.w.phillips@boeing.com>
diff --git a/plugins/org.eclipse.osee.client.integration.tests/src/org/eclipse/osee/client/integration/tests/integration/orcs/rest/ApplicabilityEndpointTest.java b/plugins/org.eclipse.osee.client.integration.tests/src/org/eclipse/osee/client/integration/tests/integration/orcs/rest/ApplicabilityEndpointTest.java
index 9b0b0c5..2a0fd35 100644
--- a/plugins/org.eclipse.osee.client.integration.tests/src/org/eclipse/osee/client/integration/tests/integration/orcs/rest/ApplicabilityEndpointTest.java
+++ b/plugins/org.eclipse.osee.client.integration.tests/src/org/eclipse/osee/client/integration/tests/integration/orcs/rest/ApplicabilityEndpointTest.java
@@ -24,7 +24,9 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import org.eclipse.osee.client.test.framework.OseeClientIntegrationRule;
 import org.eclipse.osee.client.test.framework.OseeLogMonitorRule;
 import org.eclipse.osee.framework.core.applicability.FeatureDefinition;
@@ -190,8 +192,10 @@
       // First testing by commenting instead of removing all non-applicable code
       ArtifactId productA =
          ArtifactQuery.getArtifactFromTypeAndName(CoreArtifactTypes.BranchView, "Product A", DemoBranches.SAW_PL);
-      BlockApplicabilityStageRequest data = new BlockApplicabilityStageRequest(productA, true, inputPath, stagePath);
-      appl.applyBlockVisibility(data);
+      Map<Long, String> views = new HashMap<>();
+      views.put(productA.getId(), "");
+      BlockApplicabilityStageRequest blockApplicabilityData = new BlockApplicabilityStageRequest(views, true, inputPath, stagePath);
+      appl.applyBlockVisibility(blockApplicabilityData);
 
       // Traversing the Staging Folders checking to see if each creation was successful
       File stagingFolder = new File(stagePath, "Staging");
@@ -294,9 +298,10 @@
       assertTrue(cacheFile.exists());
 
       // Set the cache file and turn off commenting for the next test
-      data.setCachePath(cacheFile.getAbsolutePath());
-      data.setCommentNonApplicableBlocks(false);
-      appl.applyBlockVisibility(data);
+      views.put(productA.getId(), cacheFile.getAbsolutePath());
+      blockApplicabilityData.setViews(views);
+      blockApplicabilityData.setCommentNonApplicableBlocks(false);
+      appl.applyBlockVisibility(blockApplicabilityData);
 
       // Testing CppTest.cpp against expected CppTest.cpp output
       cppFile = new File(codeFolder, "CppTest.cpp");
diff --git a/plugins/org.eclipse.osee.framework.core/src/org/eclipse/osee/framework/core/data/BlockApplicabilityStageRequest.java b/plugins/org.eclipse.osee.framework.core/src/org/eclipse/osee/framework/core/data/BlockApplicabilityStageRequest.java
index e10c342..1e2b31c 100644
--- a/plugins/org.eclipse.osee.framework.core/src/org/eclipse/osee/framework/core/data/BlockApplicabilityStageRequest.java
+++ b/plugins/org.eclipse.osee.framework.core/src/org/eclipse/osee/framework/core/data/BlockApplicabilityStageRequest.java
@@ -12,41 +12,37 @@
  **********************************************************************/
 package org.eclipse.osee.framework.core.data;
 
-import org.eclipse.osee.framework.jdk.core.util.Strings;
+import java.util.List;
+import java.util.Map;
 
 /**
  * @author Branden W. Phillips
  */
 public class BlockApplicabilityStageRequest {
 
-   private ArtifactId view;
+   private Map<Long, String> views;
    private boolean commentNonApplicableBlocks;
    private String sourcePath;
    private String stagePath;
-   private String cachePath = "";
+   private List<String> files;
 
    public BlockApplicabilityStageRequest() {
       // for jax-rs
    }
 
-   public BlockApplicabilityStageRequest(ArtifactId view, boolean commentNonApplicableBlocks, String sourcePath, String stagePath, String cachePath) {
-      this.view = view;
+   public BlockApplicabilityStageRequest(Map<Long, String> views, boolean commentNonApplicableBlocks, String sourcePath, String stagePath) {
+      this.views = views;
       this.commentNonApplicableBlocks = commentNonApplicableBlocks;
       this.sourcePath = sourcePath;
       this.stagePath = stagePath;
-      this.cachePath = cachePath;
    }
 
-   public BlockApplicabilityStageRequest(ArtifactId view, boolean commentNonApplicableBlocks, String sourcePath, String stagePath) {
-      this(view, commentNonApplicableBlocks, sourcePath, stagePath, Strings.EMPTY_STRING);
+   public Map<Long, String> getViews() {
+      return views;
    }
 
-   public ArtifactId getView() {
-      return view;
-   }
-
-   public void setView(ArtifactId view) {
-      this.view = view;
+   public void setViews(Map<Long, String> views) {
+      this.views = views;
    }
 
    public boolean isCommentNonApplicableBlocks() {
@@ -73,11 +69,11 @@
       this.stagePath = stagePath;
    }
 
-   public String getCachePath() {
-      return cachePath;
+   public List<String> getFiles() {
+      return files;
    }
 
-   public void setCachePath(String cachePath) {
-      this.cachePath = cachePath;
+   public void setFiles(List<String> files) {
+      this.files = files;
    }
 }
diff --git a/plugins/org.eclipse.osee.framework.jdk.core/src/org/eclipse/osee/framework/jdk/core/text/Rule.java b/plugins/org.eclipse.osee.framework.jdk.core/src/org/eclipse/osee/framework/jdk/core/text/Rule.java
index bf90141..bdfba83 100644
--- a/plugins/org.eclipse.osee.framework.jdk.core/src/org/eclipse/osee/framework/jdk/core/text/Rule.java
+++ b/plugins/org.eclipse.osee.framework.jdk.core/src/org/eclipse/osee/framework/jdk/core/text/Rule.java
@@ -187,7 +187,7 @@
    }
 
    public final void setFileNamePattern(String fileNamePattern) {
-      this.fileNamePattern = Pattern.compile(fileNamePattern);
+      this.fileNamePattern = Pattern.compile(fileNamePattern, Pattern.CASE_INSENSITIVE);
    }
 
    public final String getCharsetString() {
diff --git a/plugins/org.eclipse.osee.orcs.core/META-INF/MANIFEST.MF b/plugins/org.eclipse.osee.orcs.core/META-INF/MANIFEST.MF
index b641cb3..aef8abd 100644
--- a/plugins/org.eclipse.osee.orcs.core/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.osee.orcs.core/META-INF/MANIFEST.MF
@@ -14,6 +14,7 @@
  com.fasterxml.jackson.databind.cfg,
  com.fasterxml.jackson.databind.module,
  com.fasterxml.jackson.databind.ser.std,
+ com.fasterxml.jackson.databind.type,
  com.google.common.base,
  com.google.common.collect,
  com.google.common.io,
@@ -48,6 +49,7 @@
  org.eclipse.osee.framework.jdk.core.text.change,
  org.eclipse.osee.framework.jdk.core.type,
  org.eclipse.osee.framework.jdk.core.util,
+ org.eclipse.osee.framework.jdk.core.util.io,
  org.eclipse.osee.framework.jdk.core.util.io.xml,
  org.eclipse.osee.framework.resource.management,
  org.eclipse.osee.jaxrs,
diff --git a/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/BlockApplicabilityOps.java b/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/BlockApplicabilityOps.java
index e881132..fa443a0 100644
--- a/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/BlockApplicabilityOps.java
+++ b/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/BlockApplicabilityOps.java
@@ -13,8 +13,10 @@
 
 package org.eclipse.osee.orcs.core.internal.applicability;
 
+import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Sets;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -24,6 +26,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import javax.script.ScriptEngine;
@@ -96,11 +99,15 @@
    private final ScriptEngine se;
    private final BranchId branch;
    private final ArtifactToken view;
-   private final String plPreferences;
    private final List<FeatureDefinition> featureDefinition;
    private final Map<String, List<String>> viewApplicabilitiesMap;
-   private final Map<String, List<String>> configurationMap;
    private final boolean useCachedConfig;
+   private BlockApplicabilityRule rule;
+   private BlockApplicabilityCacheFile cache;
+   private String plPreferences;
+   private Map<String, FileTypeApplicabilityData> fileTypeApplicabilityDataMap;
+   private Map<String, List<String>> configurationMap;
+   private Map<String, Set<String>> fileApplicabilityCache = new HashMap<>();
 
    public BlockApplicabilityOps(OrcsApi orcsApi, Log logger, BranchId branch, ArtifactToken view, BlockApplicabilityCacheFile cache) {
       this.orcsApi = orcsApi;
@@ -113,6 +120,8 @@
       this.viewApplicabilitiesMap = cache.getViewApplicabilitiesMap();
       this.configurationMap = cache.getConfigurationMap();
       this.plPreferences = cache.getProductLinePreferences();
+      this.fileTypeApplicabilityDataMap = populateFileTypeApplicabilityDataMap(plPreferences);
+      this.cache = cache;
       this.useCachedConfig = true;
    }
 
@@ -128,6 +137,7 @@
          orcsApi.getQueryFactory().applicabilityQuery().getNamedViewApplicabilityMap(branch, view);
       this.configurationMap = new HashMap<>();
       this.plPreferences = getProductLinePreferences();
+      this.fileTypeApplicabilityDataMap = populateFileTypeApplicabilityDataMap(plPreferences);
       this.useCachedConfig = false;
    }
 
@@ -141,28 +151,78 @@
    public XResultData applyApplicabilityToFiles(boolean commentNonApplicableBlocks, String sourcePath, String stagePath) throws OseeCoreException {
       XResultData results = new XResultData();
 
-      Map<String, FileTypeApplicabilityData> fileTypeApplicabilityDataMap = populateFileTypeApplicabilityDataMap();
-
-      BlockApplicabilityRule rule =
-         new BlockApplicabilityRule(this, fileTypeApplicabilityDataMap, commentNonApplicableBlocks);
-
-      StringBuilder filePattern = new StringBuilder(".*\\.(");
-      filePattern.append(Collections.toString("|", fileTypeApplicabilityDataMap.keySet()));
-      filePattern.append(")");
-      rule.setFileNamePattern(filePattern.toString());
-
       HashSet<String> excludedFiles = new HashSet<>();
       excludedFiles.add("Staging");
 
-      rule.process(new File(sourcePath), stagePath, excludedFiles);
+      setUpBlockApplicability(commentNonApplicableBlocks);
+      File sourceFile = new File(sourcePath);
+      rule.process(sourceFile, stagePath, excludedFiles);
 
       if (!useCachedConfig) {
          createCacheFile(results, stagePath);
       }
 
+      writeFileApplicabilityCache(results, sourceFile.getName(), stagePath);
+
       return results;
    }
 
+   public XResultData refreshStagedFiles(String sourcePath, String stagePath, List<String> files) {
+      XResultData results = new XResultData();
+
+      File sourceDir = new File(sourcePath);
+      File stageDir = new File(stagePath, sourceDir.getName());
+
+      File fileApplicCache = readFileApplicabilityCache(results, stageDir);
+
+      Set<String> excludedFiles;
+      for (String sourceFileString : files) {
+         File sourceFile = new File(sourceDir, sourceFileString);
+         File stageFile = new File(stageDir, sourceFileString).getParentFile();
+
+         if (sourceFile.getName().equals(".fileApplicability")) {
+            /**
+             * If it's a .fileApplicability file, it could be making changes to its' siblings and therefore those all
+             * need to be processed
+             */
+            sourceFile = sourceFile.getParentFile();
+            stageFile = stageFile.getParentFile();
+         }
+
+         excludedFiles = fileApplicabilityCache.getOrDefault(stageFile.getPath(), Sets.newHashSet("Staging"));
+
+         rule.process(sourceFile, stageFile.getPath(), excludedFiles);
+      }
+
+      writeFileApplicabilityCache(results, fileApplicCache);
+
+      return results;
+
+   }
+
+   /**
+    * This method can be used internally or externally to set up the BlockApplicabilityRule class for the
+    * BlockApplicabilityTool's use.
+    */
+   public void setUpBlockApplicability(boolean commentNonApplicableBlocks) {
+      if (useCachedConfig) {
+         configurationMap = cache.getConfigurationMap();
+         plPreferences = cache.getProductLinePreferences();
+         fileTypeApplicabilityDataMap = populateFileTypeApplicabilityDataMap(plPreferences);
+      } else {
+         configurationMap = new HashMap<>();
+         plPreferences = getProductLinePreferences();
+         fileTypeApplicabilityDataMap = populateFileTypeApplicabilityDataMap(plPreferences);
+      }
+
+      rule = new BlockApplicabilityRule(this, fileTypeApplicabilityDataMap, commentNonApplicableBlocks);
+
+      StringBuilder filePattern = new StringBuilder(".*\\.(");
+      filePattern.append(Collections.toString("|", fileTypeApplicabilityDataMap.keySet()));
+      filePattern.append(")");
+      rule.setFileNamePattern(filePattern.toString());
+   }
+
    public String evaluateApplicabilityExpression(ApplicabilityBlock applic) {
       String applicabilityExpression = applic.getApplicabilityExpression();
       String toInsert = "";
@@ -453,6 +513,44 @@
       return toReturn;
    }
 
+   public void addFileApplicabilityEntry(String path, Set<String> excludedFiles) {
+      Set<String> excludedSet = new HashSet<>();
+      excludedSet.addAll(excludedFiles);
+      fileApplicabilityCache.put(path, excludedSet);
+   }
+
+   public ArtifactToken getOpsView() {
+      return view;
+   }
+
+   private File readFileApplicabilityCache(XResultData results, File stageDir) {
+      File fileApplicCache = new File(stageDir, ".fileApplicabilityCache");
+      ObjectMapper objMap = new ObjectMapper();
+      JavaType type = objMap.getTypeFactory().constructMapType(HashMap.class, String.class, Set.class);
+      try {
+         fileApplicabilityCache = objMap.readValue(fileApplicCache, type);
+      } catch (IOException ex) {
+         results.error("Error reading fileApplicabilityCache");
+      }
+
+      return fileApplicCache;
+   }
+
+   private void writeFileApplicabilityCache(XResultData results, String sourceFileName, String stagePath) {
+      File fileApplicCache = new File(stagePath, sourceFileName);
+      fileApplicCache = new File(fileApplicCache, ".fileApplicabilityCache");
+      writeFileApplicabilityCache(results, fileApplicCache);
+   }
+
+   private void writeFileApplicabilityCache(XResultData results, File fileApplicCache) {
+      ObjectMapper objMap = new ObjectMapper();
+      try {
+         objMap.writeValue(fileApplicCache, fileApplicabilityCache);
+      } catch (IOException ex) {
+         results.error("Error writing file applicability cache file");
+      }
+   }
+
    /**
     * This cache file is created to store necessary information to process applicability within this class. Anything
     * that is normally queried from the database, should be stored in this json file and saved within the stagePath.
@@ -519,7 +617,7 @@
       return preferences;
    }
 
-   private Map<String, FileTypeApplicabilityData> populateFileTypeApplicabilityDataMap() {
+   private Map<String, FileTypeApplicabilityData> populateFileTypeApplicabilityDataMap(String plPreferences) {
       Map<String, FileTypeApplicabilityData> fileTypeApplicabilityDataMap = new HashMap<>();
 
       JsonNode preferencesJson = orcsApi.jaxRsApi().readTree(plPreferences);
diff --git a/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/BlockApplicabilityRule.java b/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/BlockApplicabilityRule.java
index 6f62eef..6c9dd2e 100644
--- a/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/BlockApplicabilityRule.java
+++ b/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/BlockApplicabilityRule.java
@@ -28,6 +28,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.Stack;
+import java.util.logging.Level;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import org.eclipse.osee.framework.core.data.FileTypeApplicabilityData;
@@ -95,7 +96,7 @@
             List<File> stagedChildren = new ArrayList<>();
             stagedChildren.addAll(Arrays.asList(stageFile.listFiles()));
 
-            Set<String> filesInConfig = processConfig(children, stagedChildren, stageFile);
+            Set<String> filesInConfig = processConfig(children, stageFile);
             excludedFiles.addAll(filesInConfig);
             for (File child : children) {
                File stagedFile = process(child, stageFile.getPath(), excludedFiles);
@@ -123,30 +124,40 @@
             }
 
             // The excluded files from this scope are removed from the list
+            if (!excludedFiles.isEmpty()) {
+               orcsApplicability.addFileApplicabilityEntry(stageFile.getPath(), excludedFiles);
+            }
             excludedFiles.removeAll(filesInConfig);
          } else {
             try {
                boolean ruleWasApplicable = false;
+               File outFile = new File(stageFile.toPath() + ".tmp");
                if (fileNamePattern.matcher(inFile.getName()).matches()) {
-                  if (stageFile.exists()) {
-                     // In case this file has already been staged, we remove it before processing
-                     stageFile.delete();
-                  }
-
                   inputFile = inFile;
                   try {
-                     process(inFile, stageFile);
+                     process(inFile, outFile);
                   } catch (Exception ex) {
-                     System.out.println("Exception " + ex.toString() + " with file " + inputFile);
+                     logger.log(Level.SEVERE, "Exception " + ex.toString() + " with file " + inputFile);
                   }
                   ruleWasApplicable = this.ruleWasApplicable;
                }
-               if (!ruleWasApplicable) {
-                  Files.createLink(stageFile.toPath(), inFile.toPath());
-               } else {
-                  // Only want to set new files to read only, otherwise the original will also be read only
-                  stageFile.setReadOnly();
+
+               /**
+                * If the processed outFile is not different than what was previously in the staging, no changes are
+                * made.
+                */
+               boolean isNew = isStageFileNew(stageFile, outFile);
+               if (isNew) {
+                  if (!ruleWasApplicable) {
+                     Files.createLink(stageFile.toPath(), inFile.toPath());
+                  } else {
+                     stageFile.delete();
+                     // Only want to set new processed files to read only, otherwise the original will also be read only
+                     outFile.setReadOnly();
+                     com.google.common.io.Files.move(outFile, stageFile); // Another Files import is already taken for this class
+                  }
                }
+               outFile.delete();
             } catch (IOException ex) {
                OseeCoreException.wrap(ex);
             }
@@ -161,6 +172,35 @@
       }
    }
 
+   /**
+    * If there is no previous stage file, then it is new. <br/>
+    * If the files are not equal lengths, they must be different. <br/>
+    * Read each file line by line until a different is found, if none are found, must be the same
+    */
+   private boolean isStageFileNew(File stageFile, File outFile) throws IOException {
+
+      if (!stageFile.exists()) {
+         return true;
+      }
+
+      if (stageFile.length() != outFile.length()) {
+         return true;
+      }
+
+      BufferedReader stageReader = Files.newBufferedReader(stageFile.toPath());
+      BufferedReader outReader = Files.newBufferedReader(outFile.toPath());
+      String stageLine, outLine;
+      while (((stageLine = stageReader.readLine()) != null) && ((outLine = outReader.readLine()) != null)) {
+         if (!stageLine.equals(outLine)) {
+            if (stageFile.length() != outFile.length()) {
+               return true;
+            }
+         }
+      }
+
+      return false;
+   }
+
    @Override
    public ChangeSet computeChanges(CharSequence seq) {
       ChangeSet changeSet = new ChangeSet(seq);
@@ -324,7 +364,7 @@
     * text being left are applicable file names. If commenting is turned on, the isConfig boolean is used to fix that
     * and make sure no commenting is enabled during that file processing.
     */
-   private Set<String> processConfig(List<File> children, List<File> stagedChildren, File stageFile) {
+   private Set<String> processConfig(List<File> children, File stageFile) {
       Set<String> filesToExclude = new HashSet<>();
       BufferedReader reader;
       String readLine;
diff --git a/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/OrcsApplicabilityOps.java b/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/OrcsApplicabilityOps.java
index db8b1d0..65d146c 100644
--- a/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/OrcsApplicabilityOps.java
+++ b/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/OrcsApplicabilityOps.java
@@ -23,6 +23,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.stream.Collectors;
 import org.eclipse.osee.framework.core.applicability.ApplicabilityBranchConfig;
 import org.eclipse.osee.framework.core.applicability.BranchViewDefinition;
@@ -71,6 +72,7 @@
    private ArtifactToken featureFolder = ArtifactToken.SENTINEL;
    private ArtifactToken configurationsFolder = ArtifactToken.SENTINEL;
    private ArtifactToken plConfigurationGroupsFolder = ArtifactToken.SENTINEL;
+   private static StagedFileWatcher fileWatcher;
 
    public OrcsApplicabilityOps(OrcsApi orcsApi, Log logger) {
       this.orcsApi = orcsApi;
@@ -1448,11 +1450,120 @@
       }
 
       boolean commentNonApplicableBlocks = data.isCommentNonApplicableBlocks();
-      ArtifactId viewId = data.getView();
-      ArtifactToken viewToken;
-      BlockApplicabilityOps ops;
+      Entry<Long, String> viewInfo = data.getViews().entrySet().iterator().next();
+      ArtifactId viewId = ArtifactId.valueOf(viewInfo.getKey());
+      String cachePath = viewInfo.getValue();
+      BlockApplicabilityOps ops = getBlockApplicabilityOps(results, data, viewId, cachePath, branch);
+      if (ops == null) {
+         return results;
+      }
 
-      String cachePath = data.getCachePath();
+      stagePath = getFullStagePath(results, stagePath, ops.getOpsView().getName());
+      if (results.isErrors()) {
+         return results;
+      }
+
+      return ops.applyApplicabilityToFiles(commentNonApplicableBlocks, sourcePath, stagePath);
+   }
+
+   @Override
+   public XResultData refreshStagedFiles(BlockApplicabilityStageRequest data, BranchId branch) {
+      XResultData results = new XResultData();
+
+      String sourcePath = data.getSourcePath();
+      String stagePath = data.getStagePath();
+      List<String> files = data.getFiles();
+
+      if (sourcePath == null || stagePath == null) {
+         results.error("Both a source path and stage path are required");
+         return results;
+      }
+
+      boolean commentNonApplicableBlocks = data.isCommentNonApplicableBlocks();
+      Entry<Long, String> viewInfo = data.getViews().entrySet().iterator().next();
+      ArtifactId viewId = ArtifactId.valueOf(viewInfo.getKey());
+      String cachePath = viewInfo.getValue();
+      BlockApplicabilityOps ops = getBlockApplicabilityOps(results, data, viewId, cachePath, branch);
+      if (ops == null) {
+         return results;
+      }
+
+      stagePath = getFullStagePath(results, stagePath, ops.getOpsView().getName());
+      if (results.isErrors()) {
+         return results;
+      }
+
+      ops.setUpBlockApplicability(commentNonApplicableBlocks);
+
+      return ops.refreshStagedFiles(sourcePath, stagePath, files);
+   }
+
+   @Override
+   public XResultData startWatcher(BlockApplicabilityStageRequest data, BranchId branch) {
+      XResultData results = new XResultData();
+
+      if (fileWatcher == null) {
+         fileWatcher = new StagedFileWatcher();
+      }
+
+      String sourcePath = data.getSourcePath();
+      String stagePath = data.getStagePath();
+
+      if (sourcePath == null || stagePath == null) {
+         results.error("Both a source path and stage path are required");
+         return results;
+      }
+
+      boolean commentNonApplicableBlocks = data.isCommentNonApplicableBlocks();
+
+      for (Map.Entry<Long, String> viewInfo : data.getViews().entrySet()) {
+         ArtifactId viewId = ArtifactId.valueOf(viewInfo.getKey());
+         String cachePath = viewInfo.getValue();
+         BlockApplicabilityOps ops = getBlockApplicabilityOps(results, data, viewId, cachePath, branch);
+         if (ops == null) {
+            return results;
+         }
+
+         String fullStagePath = getFullStagePath(results, stagePath, ops.getOpsView().getName());
+         if (results.isErrors()) {
+            return results;
+         }
+
+         ops.setUpBlockApplicability(commentNonApplicableBlocks);
+         fileWatcher.addView(ops.getOpsView(), fullStagePath, ops);
+      }
+
+      Thread watcherThread = new Thread(new Runnable() {
+         @Override
+         public void run() {
+            fileWatcher.runWatcher(data, data.getSourcePath());
+         }
+      }, "Starting StagedFileWatcher");
+      watcherThread.start();
+
+      results.log("Watcher is running");
+      return results;
+   }
+
+   @Override
+   public XResultData stopWatcher() {
+      XResultData results = new XResultData();
+      if (fileWatcher == null) {
+         results.error("File Watcher has yet to be started");
+         return results;
+      }
+
+      fileWatcher.stopWatcher();
+      fileWatcher = null;
+
+      results.log("Watcher has stopped");
+      return results;
+   }
+
+   private BlockApplicabilityOps getBlockApplicabilityOps(XResultData results, BlockApplicabilityStageRequest data, ArtifactId viewId, String cachePath, BranchId branch) {
+      BlockApplicabilityOps ops = null;
+      ArtifactToken viewToken;
+
       if (cachePath.isEmpty()) {
          // The user has not given a cache to use for processing
          viewToken = orcsApi.getQueryFactory().fromBranch(branch).andId(viewId).asArtifactToken();
@@ -1467,14 +1578,14 @@
                cache = objMap.readValue(cacheFile, BlockApplicabilityCacheFile.class);
             } catch (IOException ex) {
                results.error("There was a problem reading the cache file given");
-               return results;
+               return ops;
             }
 
             Long cachedViewId = cache.getViewId();
             if (!cachedViewId.equals(viewId.getId())) {
                results.error(String.format("The entered view id (%s) does not match up with the cached view id (%s)",
                   viewId.getId(), cachedViewId));
-               return results;
+               return ops;
             }
 
             // The token is created/queried from the token service as this should not be stored within the DB
@@ -1484,24 +1595,26 @@
             ops = new BlockApplicabilityOps(orcsApi, logger, branch, viewToken, cache);
          } else {
             results.error("A cache path was given but no file was found");
-            return results;
+            return ops;
          }
       }
 
+      return ops;
+   }
+
+   private String getFullStagePath(XResultData results, String stagePath, String viewName) {
       File stageDir = new File(stagePath, "Staging");
       if (!stageDir.exists() && !stageDir.mkdir()) {
          results.error(String.format("Could not create stage directory %s", stageDir.toString()));
-         return results;
+         return "";
       }
-      File stageViewDir = new File(stageDir.getPath(), viewToken.getName().replaceAll(" ", "_"));
+      File stageViewDir = new File(stageDir.getPath(), viewName.replaceAll(" ", "_"));
       if (!stageViewDir.exists() && !stageViewDir.mkdir()) {
          results.error(String.format("Could not create stage directory %s", stageViewDir.toString()));
-         return results;
+         return "";
       }
 
-      stagePath = stageViewDir.getPath();
-
-      return ops.applyApplicabilityToFiles(commentNonApplicableBlocks, sourcePath, stagePath);
+      return stageViewDir.getPath();
    }
 
    @Override
diff --git a/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/StagedFileWatcher.java b/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/StagedFileWatcher.java
new file mode 100644
index 0000000..f981d6f
--- /dev/null
+++ b/plugins/org.eclipse.osee.orcs.core/src/org/eclipse/osee/orcs/core/internal/applicability/StagedFileWatcher.java
@@ -0,0 +1,126 @@
+/*********************************************************************
+ * Copyright (c) 2021 Boeing
+ *
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *     Boeing - initial API and implementation
+ **********************************************************************/
+package org.eclipse.osee.orcs.core.internal.applicability;
+
+import java.io.IOException;
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.osee.framework.core.data.ArtifactToken;
+import org.eclipse.osee.framework.core.data.BlockApplicabilityStageRequest;
+import org.eclipse.osee.framework.jdk.core.result.XResultData;
+import org.eclipse.osee.framework.jdk.core.type.Pair;
+
+/**
+ * This class is meant for the BlockApplicabilityTool Staging area to watch for changes within the source directory and
+ * apply them to any registered staging directory. <br/>
+ * KeyMap - Stores each WatchKey and associated Path. <br/>
+ * ViewMap - Stores each view token and maps it to a staging path and BlockApplicabilityOps, each ops class has view
+ * relevant data and cannot be shared within views. <br/>
+ *
+ * @author Branden W. Phillips
+ */
+public class StagedFileWatcher {
+
+   private WatchService watchService;
+   private final Map<WatchKey, Path> keyMap = new HashMap<>();
+   private final Map<ArtifactToken, Pair<String, BlockApplicabilityOps>> viewMap = new HashMap<>();
+
+   public StagedFileWatcher() {
+      // Do nothing
+   }
+
+   public void runWatcher(BlockApplicabilityStageRequest data, String directory) {
+      try {
+         watchService = FileSystems.getDefault().newWatchService();
+
+         Path dir = Paths.get(directory);
+
+         Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+               String fileName = dir.getFileName().toString();
+               if (!fileName.equals("Staging") && !(fileName.startsWith(".") && dir.toFile().isDirectory())) {
+                  WatchKey key = dir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
+                     StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
+                  keyMap.put(key, dir);
+                  return FileVisitResult.CONTINUE;
+               }
+               return FileVisitResult.SKIP_SUBTREE;
+            }
+         });
+
+         /**
+          * Each time a new WatchKey is gathered upon file change, first all files in that key are added to a list and
+          * then for each registered view the files are processed with the refresh method.
+          */
+         WatchKey key;
+         while ((key = watchService.take()) != null) {
+            List<String> files = new ArrayList<>();
+            for (WatchEvent<?> event : key.pollEvents()) {
+               Path path = keyMap.get(key);
+               String filePath = path.resolve((Path) event.context()).toString();
+               filePath = filePath.replace(directory, "");
+               files.add(filePath);
+            }
+            for (Map.Entry<ArtifactToken, Pair<String, BlockApplicabilityOps>> entry : viewMap.entrySet()) {
+               String stagePath = entry.getValue().getFirst();
+               BlockApplicabilityOps ops = entry.getValue().getSecond();
+               System.out.println(
+                  String.format("File Watcher has started processing files for %s", entry.getKey().getName()));
+               XResultData results = ops.refreshStagedFiles(directory, stagePath, files);
+               if (results.isErrors()) {
+                  System.out.println(results.getResults());
+               }
+               System.out.println(
+                  String.format("File Watcher has completed file processing for %s", entry.getKey().getName()));
+            }
+            key.reset();
+         }
+
+         watchService.close();
+      } catch (ClosedWatchServiceException ex) {
+         return;
+      } catch (IOException | InterruptedException ex) {
+         System.out.println(ex.getMessage());
+      }
+   }
+
+   public void stopWatcher() {
+      try {
+         watchService.close();
+         keyMap.clear();
+         viewMap.clear();
+      } catch (IOException ex) {
+         System.out.println(ex.getMessage());
+      }
+   }
+
+   public void addView(ArtifactToken view, String stagePath, BlockApplicabilityOps ops) {
+      viewMap.put(view, new Pair<String, BlockApplicabilityOps>(stagePath, ops));
+   }
+
+}
diff --git a/plugins/org.eclipse.osee.orcs.rest.model/src/org/eclipse/osee/orcs/rest/model/ApplicabilityEndpoint.java b/plugins/org.eclipse.osee.orcs.rest.model/src/org/eclipse/osee/orcs/rest/model/ApplicabilityEndpoint.java
index 7d9f47e..91d02c1 100644
--- a/plugins/org.eclipse.osee.orcs.rest.model/src/org/eclipse/osee/orcs/rest/model/ApplicabilityEndpoint.java
+++ b/plugins/org.eclipse.osee.orcs.rest.model/src/org/eclipse/osee/orcs/rest/model/ApplicabilityEndpoint.java
@@ -286,6 +286,24 @@
    @Produces(MediaType.APPLICATION_JSON)
    XResultData applyBlockVisibility(BlockApplicabilityStageRequest data);
 
+   @POST
+   @Path("blockVisibility/refresh")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @Produces(MediaType.APPLICATION_JSON)
+   XResultData refreshStagedFiles(BlockApplicabilityStageRequest data);
+
+   @PUT
+   @Path("blockVisibility/startWatcher")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @Produces(MediaType.APPLICATION_JSON)
+   XResultData startBlockVisibilityWatcher(BlockApplicabilityStageRequest data);
+
+   @PUT
+   @Path("blockVisibility/stopWatcher")
+   @Consumes(MediaType.APPLICATION_JSON)
+   @Produces(MediaType.APPLICATION_JSON)
+   XResultData stopBlockVisibilityWatcher();
+
    @PUT
    @Path("validate")
    @Produces(MediaType.APPLICATION_JSON)
diff --git a/plugins/org.eclipse.osee.orcs.rest/src/org/eclipse/osee/orcs/rest/internal/applicability/ApplicabilityEndpointImpl.java b/plugins/org.eclipse.osee.orcs.rest/src/org/eclipse/osee/orcs/rest/internal/applicability/ApplicabilityEndpointImpl.java
index 24edde4..edefdc9 100644
--- a/plugins/org.eclipse.osee.orcs.rest/src/org/eclipse/osee/orcs/rest/internal/applicability/ApplicabilityEndpointImpl.java
+++ b/plugins/org.eclipse.osee.orcs.rest/src/org/eclipse/osee/orcs/rest/internal/applicability/ApplicabilityEndpointImpl.java
@@ -398,6 +398,21 @@
    }
 
    @Override
+   public XResultData refreshStagedFiles(BlockApplicabilityStageRequest data) {
+      return ops.refreshStagedFiles(data, branch);
+   }
+
+   @Override
+   public XResultData startBlockVisibilityWatcher(BlockApplicabilityStageRequest data) {
+      return ops.startWatcher(data, branch);
+   }
+
+   @Override
+   public XResultData stopBlockVisibilityWatcher() {
+      return ops.stopWatcher();
+   }
+
+   @Override
    public ConfigurationGroupDefinition getConfigurationGroup(String id) {
       return ops.getConfigurationGroup(id, branch);
    }
diff --git a/plugins/org.eclipse.osee.orcs/src/org/eclipse/osee/orcs/OrcsApplicability.java b/plugins/org.eclipse.osee.orcs/src/org/eclipse/osee/orcs/OrcsApplicability.java
index bbe708f..3c7014c 100644
--- a/plugins/org.eclipse.osee.orcs/src/org/eclipse/osee/orcs/OrcsApplicability.java
+++ b/plugins/org.eclipse.osee.orcs/src/org/eclipse/osee/orcs/OrcsApplicability.java
@@ -95,6 +95,12 @@
 
    XResultData applyApplicabilityToFiles(BlockApplicabilityStageRequest data, BranchId branch);
 
+   XResultData refreshStagedFiles(BlockApplicabilityStageRequest data, BranchId branch);
+
+   XResultData startWatcher(BlockApplicabilityStageRequest data, BranchId branch);
+
+   XResultData stopWatcher();
+
    ConfigurationGroupDefinition getConfigurationGroup(String cfgGroup, BranchId branch);
 
    XResultData updateCfgGroup(ConfigurationGroupDefinition group, BranchId branch, UserId account);