ch.ethz.sn.visone3.io.json.JsonSource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of netroles-io-engine Show documentation
Show all versions of netroles-io-engine Show documentation
Network file IO engine for netroles library
The newest version!
/*
* This file is part of netroles.
*
* netroles is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* netroles is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with netroles. If not, see .
*/
package ch.ethz.sn.visone3.io.json;
import ch.ethz.sn.visone3.io.Source;
import ch.ethz.sn.visone3.io.SourceFormat;
import ch.ethz.sn.visone3.io.impl.IdMapper;
import ch.ethz.sn.visone3.io.impl.RangedList;
import ch.ethz.sn.visone3.io.impl.SourceFormatImpl;
import ch.ethz.sn.visone3.lang.ClassUtils;
import ch.ethz.sn.visone3.lang.Mapping;
import ch.ethz.sn.visone3.lang.Mappings;
import ch.ethz.sn.visone3.lang.PrimitiveList;
import ch.ethz.sn.visone3.networks.DyadType;
import ch.ethz.sn.visone3.networks.Network;
import ch.ethz.sn.visone3.networks.NetworkBuilder;
import ch.ethz.sn.visone3.networks.NetworkProvider;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Reads JSON graphs.
*
*
* {
* "type": ""
* "nodes": [
* {...}
* ],
* "edges": [
* {"source": id, "target": id, ...}
* ]
* }
*
*/
public class JsonSource implements Source {
private static final Logger LOG = LoggerFactory.getLogger(JsonSource.class);
static final String TYPE = "type"; // dyad type
private enum Hint {
// input key names
ID, NODES, EDGES, SOURCE, TARGET
}
/**
* Column names (hint values).
*/
private final String[] names = new String[Hint.values().length];
private final JSONObject root;
private final Map> monadic;
private final Map> dyadic;
private IdMapper> nodeIds;
private NetworkBuilder builder;
private Network incidence;
/**
* Constructs a new JSON network source.
*
* @param in
* the input stream
* @throws IOException
* if some I/O error occurs
*/
public JsonSource(final InputStream in) throws IOException {
try (Reader r = new InputStreamReader(in)) {
final JSONTokener tokener = new JSONTokener(r);
root = new JSONObject(tokener);
}
monadic = new HashMap<>();
dyadic = new HashMap<>();
autoDetect();
}
@Override
public boolean isAutoconfig() {
return true;
}
/**
* Look at some object keys to figure out how stuff is named.
*/
private void autoDetect() {
hint(Hint.ID.name(), "id");
for (final String key : root.keySet()) {
switch (key) {
case "nodes":
case "vertices":
hint(Hint.NODES.name(), key);
break;
case "links":
case "edges":
hint(Hint.EDGES.name(), key);
break;
default:
// ignore, hint not valid for this object
break;
}
}
if (key(Hint.EDGES) == null) {
return;
}
final JSONArray edges = root.getJSONArray(key(Hint.EDGES));
if (edges.length() == 0) {
return;
}
final JSONObject firstEdge = edges.getJSONObject(0);
for (final String key : firstEdge.keySet()) {
switch (key) {
case "from":
case "source":
hint(Hint.SOURCE.name(), key);
break;
case "to":
case "target":
hint(Hint.TARGET.name(), key);
break;
default:
// ignore, hint not valid for this object
break;
}
}
}
@Override
public void hint(final String key, final Object value) {
final Hint hint = Hint.valueOf(key);
names[hint.ordinal()] = (String) value;
}
private static Mapping copyIdMapping(IdMapper idMapper) {
Class cls = idMapper.getComponentType();
Mapping ids = Mappings.newListOfSizeAutoboxing(ClassUtils.unwrap(cls), idMapper.size());
idMapper.fillMapping(0, ids);
return ids;
}
@Override
public SourceFormat parse() throws IOException {
// set directionality
DyadType dt = DyadType.DIRECTED;
if (root.has(TYPE)) {
dt = DyadType.valueOf(root.getString(TYPE).toUpperCase());
}
builder = NetworkProvider.getInstance().builder(dt);
LOG.info("json hints: {}", Arrays.toString(names));
// parse the graph
parseVertices(root);
parseEdges(root);
// verify
if (!monadic.containsKey(names[Hint.ID.ordinal()])) {
monadic.put(names[Hint.ID.ordinal()], copyIdMapping(nodeIds));
}
// for (final Mapping m : monadic.values()) {
// Networks.requireVertexMapping(incidence, m);
// }
// for (final Mapping m : dyadic.values()) {
// Networks.requireLinkMapping(incidence, m);
// }
return new SourceFormatImpl(incidence, monadic, dyadic, nodeIds.getMapping());
}
@Override
public void close() {
}
/**
* From R names.
*/
private static Source.Range> componentType(final String name) {
switch (name) {
case "double":
return Source.Range.DOUBLE;
case "integer":
return Source.Range.INT;
case "character":
return Source.Range.STRING;
default:
throw new IllegalStateException("unknown type for " + name);
}
}
/**
* Create mappings for each attribute by finding the most specific type for each attribute.
*
* @param map
* a map to store the attribute mappings e.g. monadic or dyadic maps.
* @param attributeList
* list of attributes for all nodes/edges.
*/
private void createAttributeMappings(Map> map,
List