From 5e3e9d7ce7833d9472bcbf4e8977cbe3b0748ace Mon Sep 17 00:00:00 2001
From: Peter Powers <pmpowers@usgs.gov>
Date: Mon, 14 Mar 2016 12:25:14 -0600
Subject: [PATCH] site de/serializer update

---
 src/org/opensha2/calc/CalcConfig.java |   9 +-
 src/org/opensha2/calc/Sites.java      | 181 +++++++++++++++-----------
 2 files changed, 106 insertions(+), 84 deletions(-)

diff --git a/src/org/opensha2/calc/CalcConfig.java b/src/org/opensha2/calc/CalcConfig.java
index fc397f316..cf89d95ce 100644
--- a/src/org/opensha2/calc/CalcConfig.java
+++ b/src/org/opensha2/calc/CalcConfig.java
@@ -53,7 +53,7 @@ public final class CalcConfig {
 
 	private final DeaggData deagg;
 
-	private final Sites sites;
+	private final Iterable<Site> sites;
 
 	private final Map<Imt, XySequence> modelCurves;
 	private final Map<Imt, XySequence> logModelCurves;
@@ -74,7 +74,7 @@ public final class CalcConfig {
 			boolean gmmUncertainty,
 			HazardFormat hazardFormat,
 			DeaggData deagg,
-			Sites sites,
+			Iterable<Site> sites,
 			Map<Imt, XySequence> modelCurves,
 			Map<Imt, XySequence> logModelCurves) {
 
@@ -306,7 +306,7 @@ public final class CalcConfig {
 		private Boolean gmmUncertainty;
 		private HazardFormat hazardFormat;
 		private DeaggData deagg;
-		private Sites sites;
+		private Iterable<Site> sites;
 
 		/**
 		 * Initialize a new builder with a copy of that supplied.
@@ -343,7 +343,7 @@ public final class CalcConfig {
 			this.gmmUncertainty = false;
 			this.hazardFormat = HazardFormat.TOTAL;
 			this.deagg = new DeaggData();
-			this.sites = new Sites(Lists.newArrayList(Site.builder().build()));
+			this.sites = null; //new Sites(Lists.newArrayList(Site.builder().build()));
 			return this;
 		}
 
@@ -439,7 +439,6 @@ public final class CalcConfig {
 				sites,
 				curves, logCurves);
 		}
-
 	}
 
 }
diff --git a/src/org/opensha2/calc/Sites.java b/src/org/opensha2/calc/Sites.java
index 13c7146c0..cd651e4c2 100644
--- a/src/org/opensha2/calc/Sites.java
+++ b/src/org/opensha2/calc/Sites.java
@@ -1,14 +1,15 @@
 package org.opensha2.calc;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Strings.repeat;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.opensha2.geo.BorderType.MERCATOR_LINEAR;
 import static org.opensha2.util.GeoJson.validateProperty;
 import static org.opensha2.util.TextUtils.ALIGN_COL;
 import static org.opensha2.util.TextUtils.format;
 
 import java.io.IOException;
+import java.io.Reader;
 import java.lang.reflect.Type;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
@@ -30,9 +31,11 @@ import org.opensha2.util.Parsing.Delimiter;
 import org.opensha2.util.TextUtils;
 
 import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
 import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonDeserializationContext;
 import com.google.gson.JsonDeserializer;
@@ -47,72 +50,23 @@ import com.google.gson.JsonObject;
  *
  * @author Peter Powers
  */
-public final class Sites implements Iterable<Site> {
-
-	final private GriddedRegion region;
-	final private Builder builder;
-	final private List<Site> sites;
-
-	Sites(List<Site> sites) {
-		this.sites = checkNotNull(sites);
-		this.region = null;
-		this.builder = null;
-	}
-
-	Sites(GriddedRegion region, Builder builder) {
-		this.region = checkNotNull(region);
-		this.builder = checkNotNull(builder);
-		this.sites = null;
-	}
-
-	int size() {
-		return (region == null) ? sites.size() : region.size();
-	}
-
-	@Override
-	public Iterator<Site> iterator() {
-		return (region == null) ? Iterators.unmodifiableIterator(sites.iterator())
-			: new RegionIterator();
-	}
-
-	private final class RegionIterator implements Iterator<Site> {
-
-		private final Iterator<Location> locations;
-
-		private RegionIterator() {
-			locations = region.iterator();
-		}
-
-		@Override
-		public boolean hasNext() {
-			return locations.hasNext();
-		}
-
-		@Override
-		public Site next() {
-			builder.location(locations.next());
-			return builder.build();
-		}
-
-		@Override
-		public void remove() {
-			throw new UnsupportedOperationException();
-		}
-	}
+public final class Sites {
 
 	private static final int TO_STRING_LIMIT = 5;
 
-	@Override
-	public String toString() {
+	/** Custom {@code Site} iterable string formatting for logging. */
+	String logString(Iterable<Site> sites) {
+		boolean region = sites instanceof RegionIterable;
+		int size = region ? ((RegionIterable) sites).region.size() : Iterables.size(sites);
 		StringBuilder sb = new StringBuilder()
-			.append((region == null) ? "List" : "Region")
-			.append(" [size=").append(size()).append("]");
-		if (region == null) {
+			.append(region ? "Region" : "List")
+			.append(" [size=").append(size).append("]");
+		if (!region) {
 			for (Site site : Iterables.limit(sites, TO_STRING_LIMIT)) {
 				sb.append(format("Site")).append(site);
 			}
-			if (sites.size() > TO_STRING_LIMIT) {
-				int delta = sites.size() - TO_STRING_LIMIT;
+			if (size > TO_STRING_LIMIT) {
+				int delta = size - TO_STRING_LIMIT;
 				sb.append(TextUtils.NEWLINE)
 					.append(repeat(" ", ALIGN_COL + 2))
 					.append("... and ").append(delta).append(" more ...");
@@ -122,21 +76,20 @@ public final class Sites implements Iterable<Site> {
 	}
 
 	/**
-	 * Creates an {@code Iterable<Site>} from the comma-delimted site file
+	 * Create an {@code Iterable<Site>} from the comma-delimted site file
 	 * designated by {@code path}.
 	 * 
 	 * @param path to comma-delimited site data file
 	 * @throws IOException if a problem is encountered
 	 */
 	public static Iterable<Site> fromCsv(Path path) throws IOException {
-		return processCsv(path);
+		return readCsv(path);
 	}
 
-	private static Sites processCsv(Path path) throws IOException {
-		checkNotNull(path);
+	private static Iterable<Site> readCsv(Path path) throws IOException {
 
-		List<Site> siteList = new ArrayList<>();
-		Builder builder = Site.builder();
+		ImmutableList.Builder<Site> listBuilder = ImmutableList.builder();
+		Builder siteBuilder = Site.builder();
 
 		List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
 		boolean firstline = true;
@@ -174,31 +127,101 @@ public final class Sites implements Iterable<Site> {
 						lon = Double.parseDouble(value);
 						break;
 					case Site.Key.NAME:
-						builder.name(value);
+						siteBuilder.name(value);
 						break;
 					case Site.Key.VS30:
-						builder.vs30(Double.parseDouble(value));
+						siteBuilder.vs30(Double.parseDouble(value));
 						break;
 					case Site.Key.VS_INF:
-						builder.vsInferred(Boolean.parseBoolean(value));
+						siteBuilder.vsInferred(Boolean.parseBoolean(value));
 						break;
 					case Site.Key.Z1P0:
-						builder.z1p0(Double.parseDouble(value));
+						siteBuilder.z1p0(Double.parseDouble(value));
 						break;
 					case Site.Key.Z2P5:
-						builder.z2p5(Double.parseDouble(value));
+						siteBuilder.z2p5(Double.parseDouble(value));
 						break;
 					default:
 						throw new IllegalStateException("Unsupported site key: " + key);
 				}
 				index++;
 			}
-			builder.location(lat, lon);
-			siteList.add(builder.build());
+			siteBuilder.location(lat, lon);
+			listBuilder.add(siteBuilder.build());
 		}
-		return new Sites(siteList);
+		return listBuilder.build();
 	}
 
+	private static final Gson GSON = new GsonBuilder()
+		.registerTypeAdapter(Site.class, new Site.Deserializer())
+		.registerTypeAdapter(SiteIterable.class, new Deserializer())
+		.create();
+
+	/**
+	 * Create an {@code Iterable<Site>} from the GeoJSON site file
+	 * designated by {@code path}.
+	 * 
+	 * @param path to GeoJson site data file
+	 * @throws IOException if a problem is encountered
+	 */
+	public static Iterable<Site> fromJson(Path path) throws IOException {
+		Reader reader = Files.newBufferedReader(path, UTF_8);
+		SiteIterable iterable = GSON.fromJson(reader, SiteIterable.class);
+		reader.close();
+		return iterable;
+	}
+
+	/* Marker interface needed for deserialization */
+	private interface SiteIterable extends Iterable<Site> {};
+
+	private static final class ListIterable implements SiteIterable {
+		final List<Site> delegate;
+
+		ListIterable(List<Site> delegate) {
+			this.delegate = delegate;
+		}
+
+		@Override
+		public Iterator<Site> iterator() {
+			return delegate.iterator();
+		}
+	}
+	
+	private static final class RegionIterable implements SiteIterable {
+
+		final GriddedRegion region;
+		final Builder siteBuilder;
+
+		RegionIterable(GriddedRegion region, Builder siteBuilder) {
+			this.region = region;
+			this.siteBuilder = siteBuilder;
+		}
+
+		@Override
+		public Iterator<Site> iterator() {
+			return new Iterator<Site>() {
+				final Iterator<Location> locations = region.iterator();
+
+				@Override
+				public boolean hasNext() {
+					return locations.hasNext();
+				}
+
+				@Override
+				public Site next() {
+					siteBuilder.location(locations.next());
+					return siteBuilder.build();
+				}
+
+				@Override
+				public void remove() {
+					throw new UnsupportedOperationException();
+				}
+			};
+		}
+	}
+
+
 	static final String SITES = "sites";
 	static final String REGION = "region";
 	static final String BORDER = "border";
@@ -213,10 +236,10 @@ public final class Sites implements Iterable<Site> {
 	 * to lines of latitude and longitude. Polygon holes, if present are not
 	 * processed.
 	 */
-	static class Deserializer implements JsonDeserializer<Sites> {
+	static class Deserializer implements JsonDeserializer<SiteIterable> {
 
 		@Override
-		public Sites deserialize(
+		public SiteIterable deserialize(
 				JsonElement json,
 				Type type,
 				JsonDeserializationContext context) {
@@ -234,7 +257,7 @@ public final class Sites implements Iterable<Site> {
 			if (featureType.equals(GeoJson.Value.POINT)) {
 				Type siteType = new TypeToken<List<Site>>() {}.getType();
 				List<Site> sites = context.deserialize(json, siteType);
-				return new Sites(sites);
+				return new ListIterable(ImmutableList.copyOf(sites));
 			}
 
 			// or a region
@@ -296,7 +319,7 @@ public final class Sites implements Iterable<Site> {
 				spacing, spacing,
 				GriddedRegion.ANCHOR_0_0);
 
-			return new Sites(region, builder);
+			return new RegionIterable(region, builder);
 		}
 	}
 
-- 
GitLab