/* Copyright 2006 by Sean Luke and George Mason University Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package sim.app.celegans; import java.util.*; import java.io.*; import java.util.zip.*; import sim.util.*; /** Cells is the database of cell information in the program. It loads and parses cells from the data file, creating five database items: a cell dictionary (looked up by the cell's official name), a group dictionary, an expression pattern dictionary, a cell fate dictionary, and a ArrayList of lineage roots. Ordinarily, P0' would be the only root in a lineage tree, but the data is flawed and in fact several cells don't have parents and get put in as roots. Hence the need for a vector rather than a single root. Most of Cells' items are public; there's little real encapsulation here, in the name of getting the job done. */ public class Cells extends Object { private static final long serialVersionUID = 1; public HashMap cell_dictionary; // public HashMap group_dictionary; // public HashMap pattern_dictionary; // public HashMap fate_dictionary; public ArrayList roots; // Cells with no parent. There should only be one, but what the heck. int num_processed_cells; public Cell P0; public Cells() { cell_dictionary = new HashMap(300); // group_dictionary = new HashMap(); // pattern_dictionary = new HashMap(); // fate_dictionary = new HashMap(); roots = new ArrayList(); num_processed_cells=0; try { Reader r = new InputStreamReader(new GZIPInputStream(Cells.class.getResourceAsStream("cells.ace4.gz"))); readCells(r); r.close(); postProcess(); } catch (IOException e) { throw new RuntimeException(e); } } /** PostProcesses the cells after their basic information has been loaded and parsed from the input file. A <i>lot</i> of derived information is formed during postProcess(), as you can see from the method code. */ public void postProcess() { Iterator cells; /* Assign Types; report everyone who doesn't have a parent (there should only be one: P0) */ System.out.println("-----Assigning Cell Types, and Parents to equivalent-origin cells"); cells= cell_dictionary.values().iterator(); while(cells.hasNext()) { Cell cell=(Cell)cells.next(); if (cell.official_name.equals("P0")) P0 = cell; if (cell.parent==null && !cell.official_name.equals("P0") && cell.num_equivalence_origin==0) System.out.println("Whoa! This ain't right: " +cell.official_name+ "Has no parent."); if (cell.parent==null && cell.num_equivalence_origin==0) roots.add(cell); if (cell.num_equivalence_origin!=0) { cell.type=Cell.cell_type_postembryonic_dual_origin; cell.parent=cell.equivalence_origin[0]; // determining equivalence junk if (cell.parent.equivalence_fate[0]!=cell) // determining equivalence junk cell.parent.equivalence_fate[0].parent=cell.equivalence_origin[1]; else cell.parent.equivalence_fate[1].parent=cell.equivalence_origin[1]; } else if (cell.official_name.equals("P0") || cell.official_name.equals("P1'") || cell.official_name.equals("P2'") || cell.official_name.equals("AB") || cell.official_name.equals("P3'") || cell.official_name.equals("P4'")) cell.type=Cell.cell_type_preembryonic_unknown_position; else if (cell.official_name.equals("Z3") || cell.official_name.equals("Z2")) cell.type=Cell.cell_type_postembryonic_unknown_position; else if (cell.birthday<Cell.post_embryonic_birthday) /* no time born, probably no reconstruction */ cell.type=Cell.cell_type_postembryonic; else cell.type=Cell.cell_type_preembryonic; } /* First, make certain that every cell has a birthday and a death day */ System.out.println("-----Assigning Birthdays and Deaths"); cells=roots.iterator(); while(cells.hasNext()) { Cell cell=(Cell)cells.next(); cell.postProcessBirthday(true); } cells=roots.iterator(); while(cells.hasNext()) { Cell cell=(Cell)cells.next(); cell.postProcessDeathDay(true); } /* Next, I need to figure my locations in space */ System.out.println("-----Assigning Locations"); cells=roots.iterator(); while(cells.hasNext()) { Cell cell=(Cell)cells.next(); cell.postProcessLocation(true); } /* Next we move the locations around the origin. I believe the locations are located between 0 and 60 X, 0 and 40 Y, and 0 and 40 Z. So we move them by (-30,-20,-20) to center them *roughly* about the origin. */ cells=roots.iterator(); while(cells.hasNext()) { Cell cell=(Cell)cells.next(); cell.modifyLocations(-30.0,-20.0,-20.0); } /* Give Everyone a Volume. */ cells=roots.iterator(); while(cells.hasNext()) { Cell cell=(Cell) cells.next(); cell.setVolume(1.0f); } /* And we're done! */ System.out.println("-----Finished PostProcessing"); } /** Loads the cells from the input data. After this method is called, you should call postProcess() to finish the data setup */ public void readCells(Reader r) { Cell currentCell=null; ArrayList v=new ArrayList(); num_processed_cells=0; Scanner d = new Scanner(r); System.out.println("-----Loading 2237 Cells...."); System.out.println(); try { while(readCellLine(d,v)!=-1) currentCell=processCellLine(currentCell,v); } catch (IOException e) { System.out.println(e.getMessage()); } System.out.println(); System.out.println("-----Phew! Finally finished loading..."); } /** readCellLine() is used by readCells() to read a cell line from the input stream. */ int readCellLine(Scanner i, ArrayList v) throws IOException { v.clear(); if (!i.hasNextLine()) return -1; String s=i.nextLine(); // if (s==null) return -1; int pos=0; int newpos; int size=s.length(); while(true) { newpos=s.indexOf('|',pos); if (newpos<0 || newpos>=size) // Bad Index, nothing left break; if (newpos==pos) v.add(""); else v.add(s.substring(pos,newpos)); pos=newpos+1; } if (pos!=size) { // Grab last one if any v.add(s.substring(pos,size)); // is that right? } return 1; } /** processCellLine is used by readCells() to process a single cell line from the input stream. As you can tell, this is a big method, doing a lot of processing.*/ public Cell processCellLine(Cell current, ArrayList v) throws NumberFormatException { if (v==null) return current; if (v.size()==0) return current; String title=(String)v.get(0); if (title.equals("Cell")) { current=fetchCell((String)v.get(2)); if (num_processed_cells%100 == 0) System.out.println((num_processed_cells+1) + ": " + current.official_name); num_processed_cells++; } else if (current==null) return current; else { // here we go! if (title.equals("Parent")) { current.parent=fetchCell((String)v.get(1)); if (current.parent.num_children>=2) /* We've got a problem. Extend the array for this exceptional situation */ { Cell tmp[] = new Cell[current.parent.num_children+1]; /* Yeah, yeah, it's a linear increase, which is O(n^2) in the worst case, but this is an exceptional situation hopefully. */ System.arraycopy(current.parent.daughters,0,tmp,0,current.parent.num_children); current.parent.daughters=tmp; System.out.print(current.parent.official_name + " has more than 2 children: "); for(int zz=0;zz<current.parent.num_children;zz++) System.out.print(current.parent.daughters[zz].official_name + ", "); System.out.println("and " + current.official_name); } current.parent.daughters[current.parent.num_children++]=current; } else if (title.equals("Daughter")) { /* Not interested. We assume this is done by Parent */ } else if (title.equals("Lineage_name")) { current.lineage_name=(String)v.get(1); } else if (title.equals("Embryo_division_time")) { current.embryo_division_time=Double.valueOf((String)v.get(1)).doubleValue(); } else if (title.equals("Reconstruction")) { /* Two items we're interested in: Birth and Timepoint */ if (v.get(2).equals("Birth")) { current.time_born=Double.valueOf((String)v.get(3)).doubleValue(); } else if (v.get(2).equals("Timepoint")) { current. pushLocation(Double.valueOf((String)v.get(5)).doubleValue(), Double.valueOf((String)v.get(6)).doubleValue(), Double.valueOf((String)v.get(7)).doubleValue(), Double.valueOf((String)v.get(3)).doubleValue()); } } else if (title.equals("Neurodata")) { /* For the moment, we ignore Receive and Receive_Joint, assume Send and Send_Joint are equivalent, and include gap junctions. Furthermore, for lack of knowledge, we're assuming that the N2U and JSH statements are in fact different surveys (this could be TOTALLY wrong); we're going with N2U---looks more interesting. Also, we assume the last value in the neurodata line is the *number* of synapse connections. Again, this could be totally wrong. */ if (v.get(3).equals("N2U")) // what we're going with { if (v.get(2).equals("Send") || v.get(2).equals("Send_joint")) { Synapse s= new Synapse(); Cell to=fetchCell((String)v.get(1)); s.to=to; s.from=current; s.type=Synapse.type_chemical; s.number= Integer.valueOf((String)v.get(4)).intValue(); current.synapses.add(s); to.synapses.add(s); } else if (v.get(2).equals("Gap_junction")) { Synapse s= new Synapse(); Cell to=fetchCell((String)v.get(1)); s.to=to; s.from=current; s.type=Synapse.type_gap; s.number= Integer.valueOf((String)v.get(4)).intValue(); current.synapses.add(s); to.synapses.add(s); } /* I've tried to code Cell.java to ignore duplicate gap junctions. */ } } else if (title.equals("Equivalence_origin")) { Cell equiv=fetchCell((String)v.get(1)); current.equivalence_origin[current.num_equivalence_origin++]=equiv; equiv.equivalence_fate[equiv.num_equivalence_fate++]=current; } else if (title.equals("Equivalence_fate")) { /* Don't care. Done with equivalence origin. */ } else if (title.equals("Cell_group")) { current.cellGroup = fetchGroup((String)v.get(1)); } else if (title.equals("Expr_pattern")) { current.expressionPattern=fetchPattern((String)v.get(1)); } else if (title.equals("Fate")) { current.fate = fetchFate((String)v.get(1)); } else if (title.equals("Remark")) { if (current.remark.equals("")) current.remark = (String)(v.get(1)); else current.remark = current.remark + "; " + (String)(v.get(1)); } else { /* Nothing for the while */ } } return current; } /** Fetches a cell in the cell dictionary by a given key, creating a new cell if the key doesn't exist yet. */ public Cell fetchCell(String key) { // is cell already in dictionary? if (cell_dictionary.containsKey(key)) return (Cell)cell_dictionary.get(key); else { // make a new cell and add it to dictionary Cell c=new Cell(); c.official_name=key; cell_dictionary.put(key,c); return c; } } /** Fetches a group in the group dictionary by a given key, creating a new group if the key doesn't exist yet. */ public int fetchGroup(String key) { for(int i=0; i < Cell.cellGroups.length; i++) if (Cell.cellGroups[i].equalsIgnoreCase(key)) return i; System.out.println("Unknown cell group: " + key); return 0; } /** Fetches an expression pattern in the pattern dictionary by a given key, creating a new pattern if the key doesn't exist yet. */ public int fetchPattern(String key) { for(int i=0; i < Cell.expressionPatterns.length; i++) if (Cell.expressionPatterns[i].equalsIgnoreCase(key)) return i; System.out.println("Unknown expression pattern: " + key); return 0; } /** Fetches a cell fate in the fate dictionary by a given key, creating a new fate if the key doesn't exist yet. */ public int fetchFate(String key) { for(int i=0; i < Cell.fates.length; i++) if (Cell.fates[i].equalsIgnoreCase(key)) return i; System.out.println("Unknown fate: " + key); return 0; } }