reworked workspace state nesting support, general api work

deprecated WorkspaceState, all clients should use WorkspaceState2

-Dm2e.workspace.state system property value is now interpreted as
a path, this allows explicit support for nested workspace/reactors
by the host application

MutableWorkspaceState does not implicitly inherit outer workspace
state any more.

Signed-off-by: Igor Fedorenko <igor@ifedorenko.com>
diff --git a/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/MutableWorkspaceState.java b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/MutableWorkspaceState.java
index 5439b8d..1db2942 100644
--- a/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/MutableWorkspaceState.java
+++ b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/MutableWorkspaceState.java
@@ -13,21 +13,16 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.util.Properties;
+import java.util.HashMap;
 
 
 /**
  * @since 0.1
  */
-public class MutableWorkspaceState {
-
-  private final Properties state;
+public class MutableWorkspaceState extends WorkspaceState2 {
 
   public MutableWorkspaceState() {
-    // allow workspace chaining,
-    // for example, m2e launches maven build, which launches maven plugin IT
-    // IT should see artifacts from its parent maven build reactor and from m2e workspace
-    state = new Properties(WorkspaceState.getState());
+    super(new HashMap<String, String>());
   }
 
   public void putPom(File pom, String groupId, String artifactId, String version) {
@@ -58,6 +53,6 @@
    * @since 0.2
    */
   public void store(OutputStream os) throws IOException {
-    state.store(os, null);
+    asProperties().store(os, null);
   }
 }
diff --git a/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/WorkspaceState.java b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/WorkspaceState.java
index 0494344..384abeb 100644
--- a/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/WorkspaceState.java
+++ b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/WorkspaceState.java
@@ -8,104 +8,35 @@
 
 package org.eclipse.m2e.workspace;
 
-import java.io.BufferedInputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Properties;
-import java.util.Set;
 
 import org.apache.maven.artifact.Artifact;
 
 
 /**
  * @since 0.1
+ * @deprecated use {@link WorkspaceState2}
  */
 public class WorkspaceState {
-  public static final String SYSPROP_STATEFILE_LOCATION = "m2e.workspace.state";
+  public static final String SYSPROP_STATEFILE_LOCATION = WorkspaceState2.SYSPROP_STATEFILE_LOCATION;
 
-  private static Properties state;
-
-  public static synchronized Properties getState() {
-    if(state == null) {
-      state = new Properties();
-      try {
-        String location = System.getProperty(SYSPROP_STATEFILE_LOCATION);
-        if(location != null) {
-          BufferedInputStream in = new BufferedInputStream(new FileInputStream(location));
-          try {
-            state.load(in);
-          } finally {
-            in.close();
-          }
-        }
-      } catch(IOException e) {
-        // XXX log
-      }
-    }
-    return state;
+  public static Properties getState() {
+    return WorkspaceState2.getInstance().asProperties();
   }
 
   public static boolean resolveArtifact(Artifact artifact) {
-    String extension = artifact.getArtifactHandler().getExtension();
-    File file = findArtifact(artifact.getGroupId(), artifact.getArtifactId(), extension, artifact.getClassifier(),
-        artifact.getBaseVersion());
-
-    if(file == null) {
-      return false;
-    }
-
-    artifact.setFile(file);
-    artifact.setResolved(true);
-    return true;
+    return WorkspaceState2.getInstance().resolveArtifact(artifact);
   }
 
-  public static File findArtifact(String groupId, String artifactId, String type, String classifier, String baseVersion) {
-    Properties state = getState();
-    if(state == null) {
-      return null;
-    }
-
-    if(classifier == null) {
-      classifier = "";
-    }
-
-    String key = groupId + ':' + artifactId + ':' + type + ':' + classifier + ':' + baseVersion;
-    String value = state.getProperty(key);
-
-    if(value == null || value.length() == 0) {
-      return null;
-    }
-
-    File file = new File(value);
-    if(!file.exists()) {
-      return null;
-    }
-
-    return file;
+  public static File findArtifact(String groupId, String artifactId, String type, String classifier,
+      String baseVersion) {
+    return WorkspaceState2.getInstance().findArtifact(groupId, artifactId, type, classifier, baseVersion);
   }
 
   public static List<String> findVersions(String groupId, String artifactId) {
-    Properties state = getState();
-    if(state == null) {
-      return Collections.emptyList();
-    }
-
-    String prefix = groupId + ':' + artifactId + ':';
-
-    Set<String> versions = new LinkedHashSet<String>();
-    for(Object obj : state.keySet()) {
-      String key = (String) obj;
-      if(key.startsWith(prefix)) {
-        versions.add(key.substring(key.lastIndexOf(':') + 1));
-      }
-    }
-
-    return new ArrayList<String>(versions);
+    return WorkspaceState2.getInstance().findVersions(groupId, artifactId);
   }
 
 }
diff --git a/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/WorkspaceState2.java b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/WorkspaceState2.java
new file mode 100644
index 0000000..dc9a7ec
--- /dev/null
+++ b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/WorkspaceState2.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Sonatype, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.m2e.workspace;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+
+
+/**
+ * @since 0.4
+ */
+public class WorkspaceState2 {
+  public static final String SYSPROP_STATEFILE_LOCATION = "m2e.workspace.state";
+
+  protected final Map<String, String> state;
+
+  protected WorkspaceState2(Map<String, String> state) {
+    if(state == null) {
+      throw new NullPointerException();
+    }
+    this.state = state;
+  }
+
+  public Map<String, String> getState() {
+    return state;
+  }
+
+  public Properties asProperties() {
+    Properties properties = new Properties();
+    for(Map.Entry<String, String> entry : state.entrySet()) {
+      properties.put(entry.getKey(), entry.getValue());
+    }
+    return properties;
+  }
+
+  public boolean resolveArtifact(Artifact artifact) {
+    String extension = artifact.getArtifactHandler().getExtension();
+    File file = findArtifact(artifact.getGroupId(), artifact.getArtifactId(), extension, artifact.getClassifier(),
+        artifact.getBaseVersion());
+
+    if(file == null) {
+      return false;
+    }
+
+    artifact.setFile(file);
+    artifact.setResolved(true);
+    return true;
+  }
+
+  public File findArtifact(String groupId, String artifactId, String type, String classifier, String baseVersion) {
+    Map<String, String> state = getState();
+    if(state.isEmpty()) {
+      return null;
+    }
+
+    if(classifier == null) {
+      classifier = "";
+    }
+
+    String key = groupId + ':' + artifactId + ':' + type + ':' + classifier + ':' + baseVersion;
+    String value = state.get(key);
+
+    if(value == null || value.length() == 0) {
+      return null;
+    }
+
+    File file = new File(value);
+    if(!file.exists()) {
+      return null;
+    }
+
+    return file;
+  }
+
+  public List<String> findVersions(String groupId, String artifactId) {
+    Map<String, String> state = getState();
+    if(state.isEmpty()) {
+      return Collections.emptyList();
+    }
+
+    String prefix = groupId + ':' + artifactId + ':';
+
+    Set<String> versions = new LinkedHashSet<String>();
+    for(Object obj : state.keySet()) {
+      String key = (String) obj;
+      if(key.startsWith(prefix)) {
+        versions.add(key.substring(key.lastIndexOf(':') + 1));
+      }
+    }
+
+    return new ArrayList<String>(versions);
+  }
+
+  //
+  // default state
+  //
+
+  public static WorkspaceState2 load() {
+    Map<String, String> state = new HashMap<>();
+
+    String locations = System.getProperty(SYSPROP_STATEFILE_LOCATION);
+    if(locations != null) {
+      for(String location : locations.split(File.pathSeparator)) {
+        load(state, location);
+      }
+    }
+
+    return new WorkspaceState2(Collections.unmodifiableMap(state));
+  }
+
+  private static void load(Map<String, String> state, String location) {
+    try (InputStream is = new FileInputStream(location)) {
+      Properties properties = new Properties();
+      properties.load(is);
+      for(String key : properties.stringPropertyNames()) {
+        state.put(key, properties.getProperty(key));
+      }
+    } catch(IOException ex) {
+      throw new RuntimeException(ex);
+    }
+  }
+
+  private static WorkspaceState2 instance;
+
+  public static synchronized WorkspaceState2 getInstance() {
+    if(instance == null) {
+      instance = load();
+    }
+    return instance;
+  }
+}
diff --git a/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/internal/Maven30WorkspaceReader.java b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/internal/Maven30WorkspaceReader.java
index d45e0ec..e9ace3c 100644
--- a/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/internal/Maven30WorkspaceReader.java
+++ b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/internal/Maven30WorkspaceReader.java
@@ -12,10 +12,12 @@
 import java.util.List;
 
 import org.codehaus.plexus.component.annotations.Component;
-import org.eclipse.m2e.workspace.WorkspaceState;
+
 import org.sonatype.aether.repository.WorkspaceReader;
 import org.sonatype.aether.repository.WorkspaceRepository;
 
+import org.eclipse.m2e.workspace.WorkspaceState2;
+
 
 /**
  * Enables workspace resolution in Maven 3.0.x
@@ -44,12 +46,12 @@
   }
 
   public File findArtifact(org.sonatype.aether.artifact.Artifact artifact) {
-    return WorkspaceState.findArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(),
-        artifact.getClassifier(), artifact.getBaseVersion());
+    return WorkspaceState2.getInstance().findArtifact(artifact.getGroupId(), artifact.getArtifactId(),
+        artifact.getExtension(), artifact.getClassifier(), artifact.getBaseVersion());
   }
 
   public List<String> findVersions(org.sonatype.aether.artifact.Artifact artifact) {
-    return WorkspaceState.findVersions(artifact.getGroupId(), artifact.getArtifactId());
+    return WorkspaceState2.getInstance().findVersions(artifact.getGroupId(), artifact.getArtifactId());
   }
 
 }
diff --git a/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/internal/Maven31WorkspaceReader.java b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/internal/Maven31WorkspaceReader.java
index 174de7f..0329285 100644
--- a/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/internal/Maven31WorkspaceReader.java
+++ b/org.eclipse.m2e.workspace.cli/src/main/java/org/eclipse/m2e/workspace/internal/Maven31WorkspaceReader.java
@@ -17,7 +17,7 @@
 import org.eclipse.aether.repository.WorkspaceReader;
 import org.eclipse.aether.repository.WorkspaceRepository;
 
-import org.eclipse.m2e.workspace.WorkspaceState;
+import org.eclipse.m2e.workspace.WorkspaceState2;
 
 
 /**
@@ -48,12 +48,12 @@
   }
 
   public File findArtifact(org.eclipse.aether.artifact.Artifact artifact) {
-    return WorkspaceState.findArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(),
-        artifact.getClassifier(), artifact.getBaseVersion());
+    return WorkspaceState2.getInstance().findArtifact(artifact.getGroupId(), artifact.getArtifactId(),
+        artifact.getExtension(), artifact.getClassifier(), artifact.getBaseVersion());
   }
 
   public List<String> findVersions(org.eclipse.aether.artifact.Artifact artifact) {
-    return WorkspaceState.findVersions(artifact.getGroupId(), artifact.getArtifactId());
+    return WorkspaceState2.getInstance().findVersions(artifact.getGroupId(), artifact.getArtifactId());
   }
 
 }