Package org.graphstream.stream.file.dgs

Source Code of org.graphstream.stream.file.dgs.DGSParser

/*
* Copyright 2006 - 2013
*     Stefan Balev     <stefan.balev@graphstream-project.org>
*     Julien Baudry    <julien.baudry@graphstream-project.org>
*     Antoine Dutot    <antoine.dutot@graphstream-project.org>
*     Yoann Pigné      <yoann.pigne@graphstream-project.org>
*     Guilhelm Savin   <guilhelm.savin@graphstream-project.org>
*
* This file is part of GraphStream <http://graphstream-project.org>.
*
* GraphStream is a library whose purpose is to handle static or dynamic
* graph, create them from scratch, file or any source and display them.
*
* This program is free software distributed under the terms of two licenses, the
* CeCILL-C license that fits European law, and the GNU Lesser General Public
* License. You can  use, modify and/ or redistribute the software under the terms
* of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
* URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
*/
package org.graphstream.stream.file.dgs;

import java.awt.Color;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.LinkedList;

import org.graphstream.graph.implementations.AbstractElement.AttributeChangeEvent;
import org.graphstream.stream.SourceBase.ElementType;
import org.graphstream.stream.file.FileSourceDGS;
import org.graphstream.util.parser.ParseException;
import org.graphstream.util.parser.Parser;

// import org.graphstream.util.time.ISODateIO;

public class DGSParser implements Parser {
  static enum Token {
    AN, CN, DN, AE, CE, DE, CG, ST, CL, TF, EOF
  }

  protected static final int BUFFER_SIZE = 4096;

  public static final int ARRAY_OPEN = '{';
  public static final int ARRAY_CLOSE = '}';

  public static final int MAP_OPEN = '[';
  public static final int MAP_CLOSE = ']';

  Reader reader;
  int line, column;
  int bufferCapacity, bufferPosition;
  char[] buffer;
  int[] pushback;
  int pushbackOffset;
  FileSourceDGS dgs;
  String sourceId;
  Token lastDirective;

  // ISODateIO dateIO;

  public DGSParser(FileSourceDGS dgs, Reader reader) {
    this.dgs = dgs;
    this.reader = reader;
    bufferCapacity = 0;
    buffer = new char[BUFFER_SIZE];
    pushback = new int[10];
    pushbackOffset = -1;
    this.sourceId = String.format("<DGS stream %x>", System.nanoTime());

    // try {
    // dateIO = new ISODateIO();
    // } catch (Exception e) {
    // e.printStackTrace();
    // }
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.util.parser.Parser#close()
   */
  public void close() throws IOException {
    reader.close();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.util.parser.Parser#open()
   */
  public void open() throws IOException, ParseException {
    header();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.util.parser.Parser#all()
   */
  public void all() throws IOException, ParseException {
    header();

    while (next())
      ;
  }

  protected int nextChar() throws IOException {
    int c;

    if (pushbackOffset >= 0)
      return pushback[pushbackOffset--];

    if (bufferCapacity == 0 || bufferPosition >= bufferCapacity) {
      bufferCapacity = reader.read(buffer, 0, BUFFER_SIZE);
      bufferPosition = 0;
    }

    if (bufferCapacity <= 0)
      return -1;

    c = buffer[bufferPosition++];

    //
    // Handle special EOL
    // - LF
    // - CR
    // - CR+LF
    //
    if (c == '\r') {
      if (bufferPosition < bufferCapacity) {
        if (buffer[bufferPosition] == '\n')
          bufferPosition++;
      } else {
        c = nextChar();

        if (c != '\n')
          pushback(c);
      }

      c = '\n';
    }

    if (c == '\n') {
      line++;
      column = 0;
    } else
      column++;

    return c;
  }

  protected void pushback(int c) throws IOException {
    if (c < 0)
      return;

    if (pushbackOffset + 1 >= pushback.length)
      throw new IOException("pushback buffer overflow");

    pushback[++pushbackOffset] = c;
  }

  protected void skipLine() throws IOException {
    int c;

    while ((c = nextChar()) != '\n' && c >= 0)
      ;
  }

  protected void skipWhitespaces() throws IOException {
    int c;

    while ((c = nextChar()) == ' ' || c == '\t')
      ;

    pushback(c);
  }

  protected void header() throws IOException, ParseException {
    int[] dgs = new int[6];

    for (int i = 0; i < 6; i++)
      dgs[i] = nextChar();

    if (dgs[0] != 'D' || dgs[1] != 'G' || dgs[2] != 'S')
      throw parseException(String.format(
          "bad magic header, 'DGS' expected, got '%c%c%c'", dgs[0],
          dgs[1], dgs[2]));

    if (dgs[3] != '0' || dgs[4] != '0' || dgs[5] < '0' || dgs[5] > '5')
      throw parseException(String.format("bad version \"%c%c%c\"",
          dgs[0], dgs[1], dgs[2]));

    if (nextChar() != '\n')
      throw parseException("end-of-line is missing");

    skipLine();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.util.parser.Parser#next()
   */
  public boolean next() throws IOException, ParseException {
    int c;
    String nodeId;
    String edgeId, source, target;

    lastDirective = directive();

    switch (lastDirective) {
    case AN:
      nodeId = id();
      dgs.sendNodeAdded(sourceId, nodeId);

      attributes(ElementType.NODE, nodeId);
      break;
    case CN:
      nodeId = id();
      attributes(ElementType.NODE, nodeId);
      break;
    case DN:
      nodeId = id();
      dgs.sendNodeRemoved(sourceId, nodeId);
      break;
    case AE:
      edgeId = id();
      source = id();

      skipWhitespaces();
      c = nextChar();

      if (c != '<' && c != '>')
        pushback(c);

      target = id();

      switch (c) {
      case '>':
        dgs.sendEdgeAdded(sourceId, edgeId, source, target, true);
        break;
      case '<':
        dgs.sendEdgeAdded(sourceId, edgeId, target, source, true);
        break;
      default:
        dgs.sendEdgeAdded(sourceId, edgeId, source, target, false);
        break;
      }

      attributes(ElementType.EDGE, edgeId);
      break;
    case CE:
      edgeId = id();
      attributes(ElementType.EDGE, edgeId);
      break;
    case DE:
      edgeId = id();
      dgs.sendEdgeRemoved(sourceId, edgeId);
      break;
    case CG:
      attributes(ElementType.GRAPH, null);
      break;
    case ST:
      // TODO release 1.2 : read timestamp
      // Version for 1.2 :
      // --------------------------------
      // long step;
      // step = timestamp();
      // sendStepBegins(sourceId, ste);

      double step;

      step = Double.valueOf(id());
      dgs.sendStepBegins(sourceId, step);
      break;
    case CL:
      dgs.sendGraphCleared(sourceId);
      break;
    case TF:
      // TODO for release 1.2
      // String tf;
      // tf = string();

      // try {
      // dateIO.setFormat(tf);
      // } catch (Exception e) {
      // throw parseException("invalid time format \"%s\"", tf);
      // }

      break;
    case EOF:
      return false;
    }

    skipWhitespaces();
    c = nextChar();

    if (c == '#') {
      skipLine();
      return true;
    }

    if (c < 0)
      return false;

    if (c != '\n')
      throw parseException("eol expected, got '%c'", c);

    return true;
  }

  public boolean nextStep() throws IOException, ParseException {
    boolean r;
    Token next;

    do {
      r = next();
      next = directive();

      if (next != Token.EOF) {
        pushback(next.name().charAt(1));
        pushback(next.name().charAt(0));
      }
    } while (next != Token.ST && next != Token.EOF);

    return r;
  }

  protected void attributes(ElementType type, String id) throws IOException,
      ParseException {
    int c;

    skipWhitespaces();

    while ((c = nextChar()) != '\n' && c != '#' && c >= 0) {
      pushback(c);
      attribute(type, id);
      skipWhitespaces();
    }

    pushback(c);
  }

  protected void attribute(ElementType type, String elementId)
      throws IOException, ParseException {
    String key;
    Object value = null;
    int c;
    AttributeChangeEvent ch = AttributeChangeEvent.CHANGE;

    skipWhitespaces();
    c = nextChar();

    if (c == '+')
      ch = AttributeChangeEvent.ADD;
    else if (c == '-')
      ch = AttributeChangeEvent.REMOVE;
    else
      pushback(c);

    key = id();

    if (key == null)
      throw parseException("attribute key expected");

    if (ch != AttributeChangeEvent.REMOVE) {

      skipWhitespaces();
      c = nextChar();

      if (c == '=' || c == ':') {
        skipWhitespaces();
        value = value(true);
      } else {
        value = Boolean.TRUE;
        pushback(c);
      }
    }

    dgs.sendAttributeChangedEvent(sourceId, elementId, type, key, ch, null,
        value);
  }

  protected Object value(boolean array) throws IOException, ParseException {
    int c;
    LinkedList<Object> l = null;
    Object o;

    do {
      skipWhitespaces();
      c = nextChar();
      pushback(c);

      switch (c) {
      case '\'':
      case '\"':
        o = string();
        break;
      case '#':
        o = color();
        break;
      case ARRAY_OPEN:
        //
        // Skip ARRAY_OPEN
        nextChar();
        //

        skipWhitespaces();
        o = value(true);
        skipWhitespaces();

        //
        // Check if next char is ARRAY_CLOSE
        if (nextChar() != ARRAY_CLOSE)
          throw parseException("'%c' expected", ARRAY_CLOSE);
        //

        if (!o.getClass().isArray())
          o = new Object[] { o };

        break;
      case MAP_OPEN:
        o = map();
        break;
      default: {
        String word = id();

        if (word == null)
          throw parseException("missing value");

        if ((c >= '0' && c <= '9') || c == '-') {
          try {
            if (word.indexOf('.') > 0)
              o = Double.valueOf(word);
            else {
              try {
                o = Integer.valueOf(word);
              } catch (NumberFormatException e) {
                o = Long.valueOf(word);
              }
            }
          } catch (NumberFormatException e) {
            throw parseException("invalid number format '%s'", word);
          }
        } else {
          if (word.equalsIgnoreCase("true"))
            o = Boolean.TRUE;
          else if (word.equalsIgnoreCase("false"))
            o = Boolean.FALSE;
          else
            o = word;
        }

        break;
      }
      }

      c = nextChar();

      if (l == null && array && c == ',') {
        l = new LinkedList<Object>();
        l.add(o);
      } else if (l != null)
        l.add(o);
    } while (array && c == ',');

    pushback(c);

    if (l == null)
      return o;

    return l.toArray();
  }

  protected Color color() throws IOException, ParseException {
    int c;
    int r, g, b, a;
    StringBuilder hexa = new StringBuilder();

    c = nextChar();

    if (c != '#')
      throw parseException("'#' expected");

    for (int i = 0; i < 6; i++) {
      c = nextChar();

      if ((c >= 0 && c <= '9') || (c >= 'a' && c <= 'f')
          || (c >= 'A' && c <= 'F'))
        hexa.appendCodePoint(c);
      else
        throw parseException("hexadecimal value expected");
    }

    r = Integer.parseInt(hexa.substring(0, 2), 16);
    g = Integer.parseInt(hexa.substring(2, 4), 16);
    b = Integer.parseInt(hexa.substring(4, 6), 16);

    c = nextChar();

    if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
        || (c >= 'A' && c <= 'F')) {
      hexa.appendCodePoint(c);

      c = nextChar();

      if ((c >= 0 && c <= '9') || (c >= 'a' && c <= 'f')
          || (c >= 'A' && c <= 'F'))
        hexa.appendCodePoint(c);
      else
        throw parseException("hexadecimal value expected");

      a = Integer.parseInt(hexa.substring(6, 8), 16);
    } else {
      a = 255;
      pushback(c);
    }

    return new Color(r, g, b, a);
  }

  protected Object array() throws IOException, ParseException {
    int c;
    LinkedList<Object> array = new LinkedList<Object>();

    c = nextChar();

    if (c != ARRAY_OPEN)
      throw parseException("'%c' expected", ARRAY_OPEN);

    skipWhitespaces();
    c = nextChar();

    while (c != ARRAY_CLOSE) {
      pushback(c);
      array.add(value(false));

      skipWhitespaces();
      c = nextChar();

      if (c != ARRAY_CLOSE && c != ',')
        throw parseException("'%c' or ',' expected, got '%c'",
            ARRAY_CLOSE, c);

      if (c == ',') {
        skipWhitespaces();
        c = nextChar();
      }
    }

    if (c != ARRAY_CLOSE)
      throw parseException("'%c' expected", ARRAY_CLOSE);

    return array.toArray();
  }

  protected Object map() throws IOException, ParseException {
    int c;
    HashMap<String, Object> map = new HashMap<String, Object>();
    String key;
    Object value;

    c = nextChar();

    if (c != MAP_OPEN)
      throw parseException("'%c' expected", MAP_OPEN);

    c = nextChar();

    while (c != MAP_CLOSE) {
      pushback(c);
      key = id();

      if (key == null)
        throw parseException("id expected here, '%c'", c);

      skipWhitespaces();
      c = nextChar();

      if (c == '=' || c == ':') {
        skipWhitespaces();
        value = value(false);
      } else {
        value = Boolean.TRUE;
        pushback(c);
      }

      map.put(key, value);

      skipWhitespaces();
      c = nextChar();

      if (c != MAP_CLOSE && c != ',')
        throw parseException("'%c' or ',' expected, got '%c'",
            MAP_CLOSE, c);

      if (c == ',') {
        skipWhitespaces();
        c = nextChar();
      }
    }

    if (c != MAP_CLOSE)
      throw parseException("'%c' expected", MAP_CLOSE);

    return map;
  }

  protected Token directive() throws IOException, ParseException {
    int c1, c2;

    //
    // Skip comment and empty lines
    //
    do {
      c1 = nextChar();

      if (c1 == '#')
        skipLine();

      if (c1 < 0)
        return Token.EOF;
    } while (c1 == '#' || c1 == '\n');

    c2 = nextChar();

    if (c1 >= 'A' && c1 <= 'Z')
      c1 -= 'A' - 'a';

    if (c2 >= 'A' && c2 <= 'Z')
      c2 -= 'A' - 'a';

    switch (c1) {
    case 'a':
      if (c2 == 'n')
        return Token.AN;
      else if (c2 == 'e')
        return Token.AE;

      break;
    case 'c':
      switch (c2) {
      case 'n':
        return Token.CN;
      case 'e':
        return Token.CE;
      case 'g':
        return Token.CG;
      case 'l':
        return Token.CL;
      }

      break;
    case 'd':
      if (c2 == 'n')
        return Token.DN;
      else if (c2 == 'e')
        return Token.DE;

      break;
    case 's':
      if (c2 == 't')
        return Token.ST;

      break;
    case 't':
      if (c1 == 'f')
        return Token.TF;

      break;
    }

    throw parseException("unknown directive '%c%c'", c1, c2);
  }

  protected String string() throws IOException, ParseException {
    int c, s;
    StringBuilder builder;
    boolean slash;

    slash = false;
    builder = new StringBuilder();
    c = nextChar();

    if (c != '\"' && c != '\'')
      throw parseException("string expected");

    s = c;

    while ((c = nextChar()) != s || slash) {
      if (slash && c != s)
        builder.append("\\");

      slash = c == '\\';

      if (!slash) {
        if (!Character.isValidCodePoint(c))
          throw parseException("invalid code-point 0x%X", c);

        builder.appendCodePoint(c);
      }
    }

    return builder.toString();
  }

  protected String id() throws IOException, ParseException {
    int c;
    StringBuilder builder = new StringBuilder();

    skipWhitespaces();
    c = nextChar();
    pushback(c);

    if (c == '\"' || c == '\'') {
      return string();
    } else {
      boolean stop = false;

      while (!stop) {
        c = nextChar();

        switch (Character.getType(c)) {
        case Character.LOWERCASE_LETTER:
        case Character.UPPERCASE_LETTER:
        case Character.DECIMAL_DIGIT_NUMBER:
          break;
        case Character.DASH_PUNCTUATION:
          if (c != '-')
            stop = true;

          break;
        case Character.MATH_SYMBOL:
          if (c != '+')
            stop = true;

          break;
        case Character.CONNECTOR_PUNCTUATION:
          if (c != '_')
            stop = true;

          break;
        case Character.OTHER_PUNCTUATION:
          if (c != '.')
            stop = true;

          break;
        default:
          stop = true;
          break;
        }

        if (!stop)
          builder.appendCodePoint(c);
      }

      pushback(c);
    }

    if (builder.length() == 0)
      return null;

    return builder.toString();
  }

  /*
   * protected long timestamp() throws IOException, ParseException { int c;
   * String time;
   *
   * c = nextChar(); pushback(c);
   *
   * switch (c) { case '"': case '\'': time = string(); break; default:
   * StringBuilder builder = new StringBuilder();
   *
   * while ((c = nextChar()) != '\n' && c != '"') builder.appendCodePoint(c);
   *
   * pushback(c); time = builder.toString(); break; }
   *
   * pushback(c); return dateIO.parse(time).getTimeInMillis(); }
   */

  protected ParseException parseException(String message, Object... args) {
    return new ParseException(String.format(String.format(
        "parse error at (%d;%d) : %s", line, column, message), args));
  }
}
TOP

Related Classes of org.graphstream.stream.file.dgs.DGSParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.