Changeset 1974


Ignore:
Timestamp:
12/27/06 14:10:03 (13 years ago)
Author:
sorber
Message:

LoadOverlays working.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/loci/visbio/overlays/OverlayIO.java

    r1918 r1974  
    4141  // file parsing state machine states and events 
    4242  // states 
     43  protected static final int WAIT = 0; 
    4344  protected static final int TABLE = 1; 
    4445  protected static final int NODES = 2; 
     46 
     47  // events 
    4548  protected static final int BARF = -1; 
    46   protected static final int WAIT = 0; 
    47  
    48   // events 
    4949  protected static final int IGNORE = 0; 
    5050  protected static final int PARSE = 1; 
     
    5959 
    6060  // -- OverlayIO API methods -- 
    61  
    62   /** Method for determining how to handle next line read from input file */ 
    63   private static int[] getEventTypeAndNewState(String trim, int current) { 
    64     // logic for parsing overlays file 
    65     // 
    66     // I visualized this as a 'state machine' (I guess a D/NFA) producing an output  
    67     // called 'event' at each transition 
    68  
    69     int state = BARF, event = BARF; 
    70     if (current == WAIT) { 
    71       if (trim.matches("^\\s*#\\s*[Ff][Rr][Ee][Ee][Ff][Oo][Rr][Mm].*")) {state = NODES; event = INIT;} 
    72       else if (trim.matches("^\\s*$") || trim.startsWith("#")) {state = WAIT; event = IGNORE;} 
    73       else if (trim.startsWith("Overlay")) {state = TABLE; event = INIT;} 
    74     } else if (current == TABLE) { 
    75       if (trim.equals("")) {state = TABLE; event = IGNORE;} 
    76       else if (trim.matches("^\\s*#\\s*[Ff][Rr][Ee][Ee][Ff][Oo][Rr][Mm].*")) {state = NODES; event = INIT;} 
    77       else if (trim.startsWith("Line") || trim.startsWith("Freeform")  
    78           || trim.startsWith("Marker") || trim.startsWith("Text") || trim.startsWith("Oval") 
    79           || trim.startsWith("Box") || trim.startsWith("Arrow")) { 
    80         state = TABLE; 
    81         event = PARSE; 
    82       }  
    83       else if (trim.startsWith("#")) {state = TABLE; event = IGNORE;} 
    84       else { 
    85         event = BARF; 
    86         state = BARF; 
    87       } 
    88     } else if (current == NODES) { 
    89       if (trim.equals("")) {state = NODES; event = IGNORE;} 
    90       else if (trim.matches("^\\s*#\\s*[Ff][Rr][Ee][Ee][Ff][Oo][Rr][Mm].*")) {state = NODES; event = INIT;} 
    91       else if (trim.startsWith("#") || trim.matches("^[Xx]\t[Yy]")) {state = NODES; event = IGNORE;} 
    92       else if (trim.matches("^[0-9]+\\.[0-9]+\\s[0-9]+\\.[0-9]+$")) {state = NODES; event = PARSE;}  
    93       else { 
    94         state = BARF; 
    95         event = BARF; 
    96       } 
    97     } 
    98  
    99     int[] retvals = {event, state}; 
    100     return retvals; 
    101   } 
    102  
     61   
    10362  /** Reads the overlays from the given reader. */ 
    10463  public static Vector[] loadOverlays(BufferedReader in, 
    10564    OverlayTransform trans) throws IOException 
    10665  { 
    107     // housekeeping 
    10866    String[] dims = trans.getDimTypes(); 
    10967    int[] lengths = trans.getLengths(); 
    11068    JComponent owner = trans.getControls(); 
    11169 
    112     Vector[] loadedOverlays = null; 
    113     Vector loadedFreeforms = new Vector(); 
     70    // stores all overlays 
     71    Vector[] loadedOverlays = null;  
     72    boolean foundOverlays = false; 
     73     
     74    // tracks addresses of stored freeforms 
     75    Vector loadedFreeforms = new Vector();  
     76    boolean nodesChanged = false; // used in event INIT, state NODES 
     77    int numberOfFreeformsRead = 0; 
    11478    int numFreeformsRestored = 0; 
    115     boolean nodesChanged = false; 
    116     int lineNum = 0; 
    117  
     79 
     80    // tracks line number for error messages 
     81    int lineNum = 0;  
     82 
     83    // stores freeform nodes as they're read from file 
    11884    float[][] nodes = new float[2][50]; 
    11985    int numNodes = 0; 
     
    12187    int state = WAIT; 
    12288 
    123     // parse table 
     89    // read file line by line 
    12490    // actions are grouped by event type, then by current state 
    12591    while (true) { 
    12692      lineNum++; 
     93 
    12794      String line = in.readLine(); 
    12895      if (line == null) break; 
     
    133100      state = eventState[1]; 
    134101 
    135       // depending on state, parse appropriately 
    136102      if (event == IGNORE) continue; 
     103 
    137104      else if (event == BARF) { 
    138105        // barf appropriately based on state 
    139106        String s = ""; 
    140107        if (state == TABLE) s = "invalid line in overlay table"; 
    141         if (state == NODES) s = "invalid line in freeform table"; 
    142  
    143         JOptionPane.showMessageDialog(owner, "Invalid overlay file: " + s 
    144             + "\nError in following line: " + trim, 
    145             "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
     108        else if (state == NODES) s = "invalid line in freeform node lists"; 
     109        else if (state == WAIT) s = "invalid line before overlay data"; 
     110 
     111        displayErrorMsg(owner, lineNum, s); 
    146112        return null; 
     113 
    147114      } else if (event == INIT) { 
    148115        if (state == TABLE) { 
     
    150117          int count = st.countTokens(); 
    151118          // parse table header from first valid line 
    152           int numDims = count - 10; // why 10? 
     119          int numDims = count - 10; // 12: number of non-dim. fields in overlay description 
     120           
    153121          if (numDims < 0) { 
    154             JOptionPane.showMessageDialog(owner, 
    155               "Invalid table header: insufficient column headings.", 
    156               "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
     122            displayErrorMsg(owner, lineNum, "insufficient column headings"); 
    157123            return null; 
    158124          } 
     
    175141           
    176142          if (!ObjectUtil.arraysEqual(dims, theDims)) { 
    177             JOptionPane.showMessageDialog(owner, 
    178               "Invalid table header: dimensional axis types do not match.", 
    179               "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
     143            displayErrorMsg(owner, lineNum, "dimensional axis types do not match"); 
    180144            return null; 
    181145          } 
    182146          if (!ObjectUtil.arraysEqual(lengths, theLengths)) { 
    183             JOptionPane.showMessageDialog(owner, 
    184               "Invalid table header: dimensional axis lengths do not match.", 
    185               "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
    186             return null; 
    187           } 
     147            displayErrorMsg(owner, lineNum, "dimensional axis lengths do not match"); 
     148            return null; 
     149          } 
     150           
    188151          // initialize replacement overlay lists 
    189152          loadedOverlays = new Vector[MathUtil.getRasterLength(lengths)]; 
     
    191154            loadedOverlays[i] = new Vector(); 
    192155          } 
     156           
    193157        } else if (state == NODES) { 
    194           // store nodes of previously read freeform and move on 
     158          if (numberOfFreeformsRead == loadedFreeforms.size()) { 
     159            String s = "more Freeform node lists than Freeforms (" + numberOfFreeformsRead  
     160              + ") specified in table"; 
     161            displayErrorMsg(owner, lineNum, s); 
     162            return null; 
     163          } 
     164 
     165          // store nodes of previously read freeform  
    195166          if (nodesChanged) { 
    196167            OverlayFreeform of = (OverlayFreeform) loadedFreeforms.elementAt(numFreeformsRestored++); 
     
    201172            numNodes = 0; 
    202173          } 
     174          numberOfFreeformsRead++; 
    203175          nodesChanged = true; 
    204176        } 
     177 
    205178      } else if (event == PARSE) { 
    206         // parse appropriately based on state 
    207179        if (state == TABLE) { 
    208180          StringTokenizer st = new StringTokenizer("#" + line + "#", "\t"); 
     
    210182          String type = st.nextToken().substring(1); // remove initial # 
    211183          int tok = 0; 
    212  
    213           // if count is in this range it's likely essential fields are absent  
    214           if (count < lengths.length + 5) {  
    215             System.err.println("Warning: line " + lineNum + 
    216               " has an insufficient number of columns (" + count + 
    217               " instead of " + (lengths.length + 10) + ") and will be ignored. [" +  
    218               line + "]"); 
    219             continue; 
     184           
     185          if (count != lengths.length + 10) { // 10 == number of non-dim. fields in the overlay description 
     186            String s = "line in data table has an insufficient number of fields (" + count + " instead of " 
     187              + (lengths.length + 10) + ")"; 
     188            displayErrorMsg(owner, lineNum, s);  
     189            return null; 
    220190          } 
    221191 
     
    223193          for (int i=0; i<pos.length; i++) { 
    224194            try {  
    225               pos[i] = Integer.parseInt(st.nextToken()); 
    226               tok++; 
     195              int p = Integer.parseInt(st.nextToken()); 
     196              if (p >= 0 && p < lengths[i]) { // is coordinate within range? 
     197                pos[i] = p;  
     198                tok++; 
     199              } else { 
     200                pos = null; 
     201                break; 
     202              } 
    227203            } 
    228204            catch (NumberFormatException exc) { 
     
    233209 
    234210          if (pos == null) { 
    235             System.err.println("Warning: line " + lineNum + 
    236               " has an invalid dimensional position and will be ignored."); 
    237             continue; 
     211            displayErrorMsg(owner, lineNum, "line has an invalid dimensional position"); 
     212            return null; 
    238213          } 
    239214           
    240215          String sx1, sx2, sy1, sy2; 
    241216 
    242           try { 
    243             sx1 = st.nextToken();  
    244             sy1 = st.nextToken(); 
    245             sx2 = st.nextToken();  
    246             sy2 = st.nextToken(); 
    247           } catch (NoSuchElementException exc) { 
    248             JOptionPane.showMessageDialog(owner, "Invalid overlay file: missing XY coordinate(s) in line " 
    249                 +  lineNum, "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
    250             return null; 
    251           } 
    252  
     217          sx1 = st.nextToken();  
     218          sy1 = st.nextToken(); 
     219          sx2 = st.nextToken();  
     220          sy2 = st.nextToken(); 
    253221             
    254222          float x1, y1, x2, y2; 
     
    262230 
    263231          catch (NumberFormatException exc) { 
    264             System.err.println("Warning: line " + lineNum + 
    265               " has invalid coordinate values and will be ignored."); 
    266             System.err.println(sx1+sy1+sx2+sy2); 
     232            displayErrorMsg(owner, lineNum, "line has invalid coordinate values"); 
     233            return null; 
     234          } 
     235           
     236          String text = st.nextToken(); 
     237 
     238          Color color; 
     239          try { 
     240            color = ColorUtil.hexToColor(st.nextToken()); 
     241          } catch (NumberFormatException exc) { 
     242            displayErrorMsg(owner, lineNum, "line has invalid color value"); 
    267243            continue; 
    268244          } 
    269            
    270           String text; 
    271           try { text = st.nextToken(); } catch (NoSuchElementException exc) {  
    272             System.err.println("Warning: line " + lineNum + 
    273               " has an invalid text value."); 
    274             text = "N\\A"; 
    275           } 
    276  
    277           Color color; 
    278           try { color = ColorUtil.hexToColor(st.nextToken()); } catch (NoSuchElementException exc) { 
    279             System.err.println("Warning: line " + lineNum + 
    280               " has an invalid color value: color will be set to #ffffff."); 
    281             color = ColorUtil.hexToColor("ffffff"); 
    282           } 
    283245 
    284246          boolean filled = false; 
    285           try { filled = st.nextToken().equalsIgnoreCase("true"); } catch (NoSuchElementException exc) { 
    286             System.err.println("Warning: line " + lineNum + 
    287               " has an invalid value for field 'filled': will be set to false."); 
    288             color = ColorUtil.hexToColor("ffffff"); 
    289           } 
     247          filled = st.nextToken().equalsIgnoreCase("true");  
    290248   
    291           String group = "None"; 
    292           try {group = st.nextToken();} catch (NoSuchElementException exc) { 
    293             System.err.println("Warning: line " + lineNum + 
    294               " has an invalid value for field 'group' and will be set to 'None'."); 
    295           } 
     249          String group = st.nextToken(); 
    296250 
    297251          String notes = ""; 
    298           try {  
    299             notes = st.nextToken();  
    300             notes = notes.substring(0, notes.length() - 1); // remove trailing # 
    301           } catch (NoSuchElementException exc) { 
    302             System.err.println("warning: line " + lineNum + 
    303               " has an invalid value for field 'notes' (possibly a missing trailing [tab]."); 
    304           } 
    305  
    306           if (st.hasMoreTokens()) { 
    307             System.err.println("warning: line " + lineNum + 
    308               " has extra fields."); 
    309           } 
    310  
     252          notes = st.nextToken();  
     253          notes = notes.substring(0, notes.length() - 1); // remove trailing # 
    311254 
    312255          String className = "loci.visbio.overlays.Overlay" + type; 
     
    320263          } 
    321264 
     265          int r = MathUtil.positionToRaster(lengths, pos); 
     266          // this error should never fire--will be caught above ("is coordinate w/in range?") 
     267          /* 
     268          if (r < 0 || r >= loadedOverlays.length) { 
     269            displayErrorMsg(owner, lineNum, "could not reconstruct overlay: invalid dimensional position"); 
     270            return null; 
     271          } 
     272          */ 
     273           
    322274          // assign overlay parameters 
    323           int r = MathUtil.positionToRaster(lengths, pos); 
    324           if (r < 0 || r >= loadedOverlays.length) { 
    325             System.err.println("Warning: could not reconstruct " + type + 
    326               "overlay defined on line " + lineNum + 
    327               ": invalid dimensional position."); 
    328             continue; 
    329           } 
    330            
    331275          obj.x1 = x1; 
    332276          obj.y1 = y1; 
     
    345289          // add overlay to list 
    346290          loadedOverlays[r].add(obj); 
    347            
     291          foundOverlays = true; 
    348292        } else if (state == NODES) { 
    349           /* 
    350           JOptionPane.showMessageDialog(owner, "Invalid overlay file: missing node lists for one or more Freeforms.", "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
    351           return null; 
    352           */ 
    353          
    354           // try to parse floats 
     293 
    355294          String[] toks = trim.split("\\s");// split on whitespace 
    356           if (toks.length != 2)  { 
    357             JOptionPane.showMessageDialog(owner, "Invalid overlay file: Unexpected token near node coordinates", "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
    358             return null; 
    359           } 
    360  
     295           
    361296          float x, y; 
    362297          try { 
     
    364299            y = Float.parseFloat(toks[1]); 
    365300          }  
    366           catch (NumberFormatException exc) { // if parseFloat fails 
    367             JOptionPane.showMessageDialog(owner, "Invalid overlay file: " + 
    368                 "malformatted Freeform node list.\nError found near following string: " + trim,  
    369                 "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
     301          catch (NumberFormatException exc) {  
     302            // this error message won't fire: covered by regular expressions in getEventAndState 
     303            displayErrorMsg(owner, lineNum, "error parsing node coordinates");  
    370304            return null; 
    371305          } 
     
    381315          } 
    382316        } 
    383       } // end event == PARSE 
     317      } // end (event == PARSE) 
    384318    } // end while(true) 
    385319 
    386       
    387     if (numFreeformsRestored + 1 < loadedFreeforms.size()) { 
    388       JOptionPane.showMessageDialog(owner, "Invalid overlay file: missing node lists for one or more Freeforms.",  
    389           "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
     320    // after parsing all lines:  
     321    if (!foundOverlays) { 
     322      displayErrorMsg(owner, lineNum, "no overlays found"); 
    390323      return null; 
    391     } else if (numFreeformsRestored == loadedFreeforms.size()) { // >= should cause a problem earlier 
    392       JOptionPane.showMessageDialog(owner, "Invalid overlay file: more node lists than Freeforms" +  
    393           " specified in table.",  
    394           "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
    395       return null; 
    396     } else { 
    397       // store last freeform read 
    398       OverlayFreeform of = (OverlayFreeform) loadedFreeforms.elementAt(numFreeformsRestored++); 
    399       float[][] temp = new float[2][numNodes]; 
    400       for (int i=0; i<2; i++) System.arraycopy(nodes[i], 0, temp[i], 0, numNodes); 
    401       of.setNodes(temp); 
    402     } 
    403  
    404     if (loadedOverlays == null) { 
    405       JOptionPane.showMessageDialog(owner, 
    406         "Invalid overlay file: no table header found.", 
    407         "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
    408       return null; 
     324    } else if (loadedFreeforms != null) { 
     325      if (numFreeformsRestored + 1 < loadedFreeforms.size()) { 
     326        displayErrorMsg(owner, lineNum, "missing node lists for one or more Freeforms");  
     327        return null; 
     328      } else { 
     329        // store last freeform read 
     330        OverlayFreeform of = (OverlayFreeform) loadedFreeforms.elementAt(numFreeformsRestored++); 
     331        float[][] temp = new float[2][numNodes]; 
     332        for (int i=0; i<2; i++) System.arraycopy(nodes[i], 0, temp[i], 0, numNodes); 
     333        of.setNodes(temp); 
     334      } 
    409335    } 
    410336 
     
    413339  } 
    414340   
    415   /* 
    416   private static void parseFreeformBlock(BufferedReader in, Vector loadedOverlays) { 
    417  
    418     String l = in.readLine(); 
    419     String lt = l.trim(); 
    420  
    421     while (!lt.equals("\n")) { 
    422       if (lt.matches("X\tY")) continue; 
    423         StringTokenizer st = new StringTokenizer (lt, "\t"); 
    424         if (st.countTokens != 2) { 
    425           JOptionPane.showMessageDialog(owner, "Invalid overlay file: malformatted Freeform node list.\n"  
    426               + "Error found near following string: " + line + "", "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
    427           return null; 
    428         } 
    429  
    430         String x, y; 
    431         try { 
    432           x = node.substring(1, comma); 
    433           y = node.substring(comma + 1, node.length()-1); 
    434           nodes[0][i] = Float.parseFloat(x);         
    435           nodes[1][i] = Float.parseFloat(y); 
    436           i++; 
    437         } catch (StringIndexOutOfBoundsException exc) { // if no comma, extra spaces 
    438           JOptionPane.showMessageDialog(owner, "Invalid overlay file: malformatted Freeform node list.\n"  
    439               + "Error found near following string: " + node + "", "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
    440           return null; 
    441         } catch (NumberFormatException exc) { // if parseFloat fails 
    442           JOptionPane.showMessageDialog(owner, "Invalid overlay file: malformatted Freeform node list.\n"  
    443               + "Error found near following string: " + node + "", "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
    444           return null; 
    445         } 
    446     } 
    447   } 
    448   */ 
    449  
    450341  /** Writes the overlays to the given writer. */ 
    451342  public static void saveOverlays(PrintWriter out, OverlayTransform trans) { 
     
    499390    } 
    500391 
    501     // nodes of freeforms, one freeform per line 
     392    // nodes of freeforms, one node per line 
    502393    for (int i=0; i<savedFreeforms.size(); i++) { 
    503394      OverlayFreeform of = (OverlayFreeform) savedFreeforms.get(i); 
     
    508399      xx2 = of.getX2(); 
    509400      yy2 = of.getY2(); 
     401      // nodes header 
    510402      out.println("# Freeform line " + i + " (" + xx1 + "," + yy1 + ")(" + xx2 + "," + yy2 + ")"); 
    511403      out.println("X\tY"); 
     
    570462  } 
    571463 
     464 
     465  // -- Private helper methods -- 
     466   
     467  /** Method for determining how to handle next line read from input file */ 
     468  private static int[] getEventTypeAndNewState(String input, int current) { 
     469    // logic for parsing overlays file --ACS 12/06 
     470    //  
     471    // I visualized the process of parsing an overlay file as a 'state machine': 
     472    // at each input (a line of text), the machine changes state and spits out an event 
     473    // telling the method loadOverlays to parse the line, display an error message, etc. 
     474    // This method getEventTypeAndNewState describes the state machine's behavior: each  
     475    // top-level if/elseif clause corresponds to a different state, and the interior 
     476    // if/elseif/else clauses describe possible transitions from that state. 
     477    // 
     478    // As a result of the state machine, I've managed to put most the error messages for 
     479    // unexpected lines in one place (if (event == BARF) under loadOverlays); however 
     480    // there are still many cases which generate errors elsewhere in loadOverlays. 
     481    // Adding more states to the machine and/or more rigorous checking for acceptable line formats 
     482    // in this machine would help reduce the number of exceptional cases not handled here. 
     483 
     484    int state = WAIT, event = BARF; 
     485    if (current == WAIT) { 
     486      if (input.matches("^\\s*$") || input.startsWith("#")) {state = WAIT; event = IGNORE;} 
     487      else if (input.startsWith("Overlay")) {state = TABLE; event = INIT;} 
     488      else {state = WAIT; event = BARF;} 
     489    } else if (current == TABLE) { 
     490      if (input.equals("")) {state = TABLE; event = IGNORE;} 
     491      else if (input.matches("^\\s*#\\s*[Ff][Rr][Ee][Ee][Ff][Oo][Rr][Mm].*")) {state = NODES; event = INIT;} 
     492      else if (input.startsWith("Line") || input.startsWith("Freeform")  
     493          || input.startsWith("Marker") || input.startsWith("Text") || input.startsWith("Oval") 
     494          || input.startsWith("Box") || input.startsWith("Arrow")) { 
     495        state = TABLE; 
     496        event = PARSE; 
     497      }  
     498      else if (input.startsWith("#")) {state = TABLE; event = IGNORE;} // must check for freeform header first 
     499      else { 
     500        event = BARF; 
     501        state = TABLE; 
     502      } 
     503    } else if (current == NODES) { 
     504      if (input.equals("")) {state = NODES; event = IGNORE;} 
     505      else if (input.matches("^\\s*#\\s*[Ff][Rr][Ee][Ee][Ff][Oo][Rr][Mm].*")) {state = NODES; event = INIT;} 
     506      else if (input.startsWith("#") || input.matches("^[Xx]\t[Yy]")) {state = NODES; event = IGNORE;} 
     507      else if (input.matches("^[0-9]+\\.[0-9]+\\s[0-9]+\\.[0-9]+$")) {state = NODES; event = PARSE;}  
     508      else { 
     509        state = NODES; 
     510        event = BARF; 
     511      } 
     512    } 
     513 
     514    int[] retvals = {event, state}; 
     515    return retvals; 
     516  } 
     517 
     518  /** Displays an alarm box */ 
     519  private static void displayErrorMsg(JComponent owner, int line, String msg) { 
     520    JOptionPane.showMessageDialog(owner, "Invalid overlay file: "  
     521      + msg + "\n" + "Error in line " + line , 
     522      "Cannot load overlays", JOptionPane.ERROR_MESSAGE); 
     523  } 
     524 
    572525} 
Note: See TracChangeset for help on using the changeset viewer.