/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview 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.
*
* Jalview 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 Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.io;
import jalview.bin.Cache;
import jalview.datamodel.DBRefEntry;
import jalview.datamodel.SequenceI;
import java.util.List;
import com.stevesoft.pat.Regex;
public class ModellerDescription
{
/**
* Translates between a String containing a set of colon-separated values on a
* single line, and sequence start/end and other properties. See PIRFile IO
* for its use.
*/
final String[] seqTypes = { "sequence", "structure", "structureX",
"structureN" };
final String[] Fields = { "objectType", "objectId", "startField",
"startCode", "endField", "endCode", "description1", "description2",
"resolutionField", "tailField" };
final int TYPE = 0;
final int LOCALID = 1;
final int START = 2;
final int START_CHAIN = 3;
final int END = 4;
final int END_CHAIN = 5;
final int DESCRIPTION1 = 6;
final int DESCRIPTION2 = 7;
final int RESOLUTION = 8;
final int TAIL = 9;
/**
* 0 is free text or empty 1 is something that parses to an integer, or \@
*/
final int Types[] = { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0 };
final char Padding[] = { ' ', ' ', ' ', '.', ' ', '.', '.', '.', '.',
'.' };
java.util.Hashtable fields = new java.util.Hashtable();
ModellerDescription()
{
fields.put(Fields[TAIL], "");
}
class resCode
{
Integer val;
String field;
resCode(String f, Integer v)
{
val = v;
field = f;
}
resCode(int v)
{
val = Integer.valueOf(v);
field = val.toString();
}
};
private resCode validResidueCode(String field)
{
Integer val = null;
Regex r = new Regex("\\s*((([-0-9]+).?)|FIRST|LAST|@)");
if (!r.search(field))
{
return null; // invalid
}
String value = r.stringMatched(3);
if (value == null)
{
value = r.stringMatched(1);
}
// Cache.debug("from '" + field + "' matched '" + value +
// "'");
try
{
val = Integer.valueOf(value);
return new resCode(field, val); // successful numeric extraction
} catch (Exception e)
{
}
return new resCode(field, null);
}
private java.util.Hashtable parseDescription(String desc)
{
java.util.Hashtable fields = new java.util.Hashtable();
java.util.StringTokenizer st = new java.util.StringTokenizer(desc, ":",
true);
String field;
int type = -1;
if (st.countTokens() > 0)
{
// parse colon-fields
int i = 0;
field = st.nextToken(":");
do
{
if (seqTypes[i].equalsIgnoreCase(field))
{
break;
}
} while (++i < seqTypes.length);
if (i < seqTypes.length)
{
st.nextToken(); // skip ':'
// valid seqType for modeller
type = i;
i = 1; // continue parsing fields
while (i < TAIL && st.hasMoreTokens())
{
if ((field = st.nextToken(":")) != null)
{
if (!field.equals(":"))
{
// validate residue field value
if (Types[i] == 1)
{
resCode val = validResidueCode(field);
if (val != null)
{
fields.put(new String(Fields[i] + "num"), val);
}
else
{
// Cache.debug(
// "Ignoring non-Modeller description: invalid integer-like
// field '" + field + "'");
type = -1; /* invalid field! - throw the FieldSet away */
}
;
}
fields.put(Fields[i++], field);
if (st.hasMoreTokens())
{
st.nextToken(); // skip token sep.
}
}
else
{
i++;
}
}
}
if (i == TAIL)
{
// slurp remaining fields
while (st.hasMoreTokens())
{
String tl = st.nextToken(":");
field += tl.equals(":") ? tl : (":" + tl);
}
fields.put(Fields[TAIL], field);
}
}
}
if (type == -1)
{
// object is not a proper ModellerPIR object
fields = new java.util.Hashtable();
fields.put(Fields[TAIL], new String(desc));
}
else
{
fields.put(Fields[TYPE], seqTypes[type]);
}
return fields;
}
ModellerDescription(String desc)
{
if (desc == null)
{
desc = "";
}
fields = parseDescription(desc);
}
void setStartCode(int v)
{
resCode r;
fields.put(Fields[START] + "num", r = new resCode(v));
fields.put(Fields[START], r.field);
}
void setEndCode(int v)
{
resCode r;
fields.put(Fields[END] + "num", r = new resCode(v));
fields.put(Fields[END], r.field);
}
/**
* make a possibly updated modeller field line for the sequence object
*
* @param seq
* SequenceI
*/
ModellerDescription(SequenceI seq)
{
if (seq.getDescription() != null)
{
fields = parseDescription(seq.getDescription());
}
if (isModellerFieldset())
{
// Set start and end before we update the type (in the case of a
// synthesized field set)
if (getStartCode() == null || (getStartNum() != seq.getStart()
&& getStartCode().val != null))
{
// unset or user updated sequence start position
setStartCode(seq.getStart());
}
if (getEndCode() == null || (getEndNum() != seq.getEnd()
&& getStartCode() != null && getStartCode().val != null))
{
setEndCode(seq.getEnd());
}
}
else
{
// synthesize fields
setStartCode(seq.getStart());
setEndCode(seq.getEnd());
fields.put(Fields[LOCALID], seq.getName()); // this may be overwritten
// below...
// type - decide based on evidence of PDB database references - this also
// sets the local reference field
int t = 0; // sequence
if (seq.getDatasetSequence() != null
&& seq.getDatasetSequence().getDBRefs() != null)
{
List dbr = seq.getDatasetSequence().getDBRefs();
for (int i = 0, ni = dbr.size(); i < ni; i++)
{
DBRefEntry dbri = dbr.get(i);
if (dbri != null)
{
// JBPNote PDB dbRefEntry needs properties to propagate onto
// ModellerField
// JBPNote Need to get info from the user about whether the sequence
// is the one being modelled, or if it is a template.
if (dbri.getSource().equals(jalview.datamodel.DBRefSource.PDB))
{
fields.put(Fields[LOCALID], dbri.getAccessionId());
t = 2;
break;
}
}
}
}
fields.put(Fields[TYPE], seqTypes[t]);
}
}
/**
* Indicate if fields parsed to a modeller-like colon-separated value line
*
* @return boolean
*/
boolean isModellerFieldset()
{
return (fields.containsKey(Fields[TYPE]));
}
String getDescriptionLine()
{
String desc = "";
int lastfield = Fields.length - 1;
if (isModellerFieldset())
{
String value;
// try to write a minimal modeller field set, so..
// find the last valid field in the entry
for (; lastfield > 6; lastfield--)
{
if (fields.containsKey(Fields[lastfield]))
{
break;
}
}
for (int i = 0; i < lastfield; i++)
{
value = (String) fields.get(Fields[i]);
if (value != null && value.length() > 0)
{
desc += ((String) fields.get(Fields[i])) + ":";
}
else
{
desc += Padding[i] + ":";
}
}
}
// just return the last field if no others were defined.
if (fields.containsKey(Fields[lastfield]))
{
desc += (String) fields.get(Fields[lastfield]);
}
else
{
desc += ".";
}
return desc;
}
int getStartNum()
{
int start = 0;
resCode val = getStartCode();
if (val != null && val.val != null)
{
return val.val.intValue();
}
return start;
}
resCode getStartCode()
{
if (isModellerFieldset() && fields.containsKey(Fields[START] + "num"))
{
return (resCode) fields.get(Fields[START] + "num");
}
return null;
}
resCode getEndCode()
{
if (isModellerFieldset() && fields.containsKey(Fields[END] + "num"))
{
return (resCode) fields.get(Fields[END] + "num");
}
return null;
}
int getEndNum()
{
int end = 0;
resCode val = getEndCode();
if (val != null && val.val != null)
{
return val.val.intValue();
}
return end;
}
/**
* returns true if sequence object was modifed with a valid modellerField set
*
* @param newSeq
* SequenceI
* @return boolean
*/
boolean updateSequenceI(SequenceI newSeq)
{
if (isModellerFieldset())
{
resCode rc = getStartCode();
if (rc != null && rc.val != null)
{
newSeq.setStart(getStartNum());
}
else
{
newSeq.setStart(1);
}
rc = getEndCode();
if (rc != null && rc.val != null)
{
newSeq.setEnd(getEndNum());
}
else
{
newSeq.setEnd(newSeq.getStart() + newSeq.getLength());
}
return true;
}
return false;
}
}