Clover icon

Coverage Report

  1. Project Clover database Thu Jun 4 2026 14:16:38 BST
  2. Package jalview.io

File LayeredDBNParser.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

24
84
18
5
338
232
30
0.36
4.67
3.6
1.67

Classes

Class Line # Actions
LayeredDBNParser 34 43 15
0.716417971.6%
LayeredDBNParser.Chain 58 6 3
0.550%
LayeredDBNParser.CompactBp 88 3 2
0.00%
LayeredDBNParser.BpSpec 111 8 7
0.312531.2%
LayeredDBNParser.Record 155 24 3
0.6428571364.3%
 

Contributing tests

This file is covered by 3 tests. .

Source view

1    package jalview.io;
2   
3    import java.io.IOException;
4    import java.util.ArrayList;
5    import java.util.Arrays;
6    import java.util.LinkedHashMap;
7    import java.util.List;
8    import java.util.Map;
9    import java.util.regex.Matcher;
10    import java.util.regex.Pattern;
11   
12    import jalview.datamodel.AlignmentAnnotation;
13    import jalview.datamodel.Sequence;
14    import jalview.datamodel.SequenceI;
15   
16    /**
17    * Gemini Canvas Pro (3.1 2026/05/28) generated fragment from prompt:
18    *
19    * write a java fragment that will parse the following structured text: Extended
20    * Fasta like format structured format with structured header, and colon
21    * separated tagged payload: >title: chain <code>(start-end)[, chain
22    * <code>(start-end)]+ seq : <SEQ..>[&<SEQ..>]+ <bpt_1> : <WUSS>[&<WUSS>]+
23    * <bpt_n> : <WUSS>[&<WUSS>]+
24    *
25    * Pairing coordinate system is defined on the 'seq' line beginning with the
26    * column that is the first non-white space character (or could be at most one
27    * space after colon before start of (extended)WUSS-style dbn.
28    *
29    * <bpt> are LW-base pair classification codes (see
30    * https://nakb.org/basics/bases.html). Convention is that canonical (cWW)
31    * interactions are specified first.
32    *
33    */
 
34    public class LayeredDBNParser extends AlignFile
35    {
36   
 
37  1 toggle public LayeredDBNParser(String dataObject, DataSourceType sourceType)
38    throws IOException
39    {
40  1 super(dataObject, sourceType);
41    }
42   
 
43  3 toggle public LayeredDBNParser(FileParse source) throws IOException
44    {
45  3 super(source);
46    }
47   
 
48  0 toggle public LayeredDBNParser()
49    {
50    // TODO Auto-generated constructor stub
51    }
52   
53    private Record parsedRecord;
54   
55    /**
56    * Represents a parsed Chain with its code, start, and end coordinates.
57    */
 
58    public static class Chain
59    {
60    public final String code;
61   
62    public final String identifier;
63   
64    public final int start;
65   
66    public final int end;
67   
 
68  8 toggle public Chain(String code, String identifier, int start, int end)
69    {
70  8 this.code = code;
71  8 this.identifier = identifier;
72  8 this.start = start;
73  8 this.end = end;
74    }
75   
 
76  0 toggle @Override
77    public String toString()
78    {
79  0 String idStr = (identifier != null) ? "[" + identifier + "]" : "";
80  0 return String.format("Chain{code='%s'%s, start=%d, end=%d}", code,
81    idStr, start, end);
82    }
83    }
84   
85    /**
86    * Represents a single compact basepair tuple: (position, bracket)
87    */
 
88    public static class CompactBp
89    {
90    public final int position;
91   
92    public final char bracket;
93   
 
94  0 toggle public CompactBp(int position, char bracket)
95    {
96  0 this.position = position;
97  0 this.bracket = bracket;
98    }
99   
 
100  0 toggle @Override
101    public String toString()
102    {
103  0 return String.format("(%d, %c)", position, bracket);
104    }
105    }
106   
107    /**
108    * Encapsulates a basepair specification that is either a WUSS string OR a
109    * list of compact (number, bracket) representations.
110    */
 
111    public static class BpSpec
112    {
113    private final String wuss;
114   
115    private final List<CompactBp> compacts;
116   
 
117  48 toggle public BpSpec(String wuss)
118    {
119  48 this.wuss = wuss;
120  48 this.compacts = null;
121    }
122   
 
123  0 toggle public BpSpec(List<CompactBp> compacts)
124    {
125  0 this.wuss = null;
126  0 this.compacts = compacts;
127    }
128   
 
129  0 toggle public boolean isWuss()
130    {
131  0 return wuss != null;
132    }
133   
 
134  60 toggle public String getWuss()
135    {
136  60 return wuss;
137    }
138   
 
139  0 toggle public List<CompactBp> getCompacts()
140    {
141  0 return compacts;
142    }
143   
 
144  0 toggle @Override
145    public String toString()
146    {
147  0 return isWuss() ? "WUSS[" + wuss + "]"
148    : "Compact" + compacts.toString();
149    }
150    }
151   
152    /**
153    * Represents a fully parsed record from the extended FASTA format.
154    */
 
155    public static class Record
156    {
157    public String title;
158   
159    public List<Chain> chains = new ArrayList<>();
160   
161    // Split sequence segments (separated by '&' in the file)
162    public List<String> sequences = new ArrayList<>();
163   
164    // Base pair types (e.g., cWW, tWH) mapped to their respective BpSpec
165    // segments.
166    // LinkedHashMap is used to preserve the insertion order (canonical first).
167    public Map<String, List<BpSpec>> basePairs = new LinkedHashMap<>();
168   
 
169  0 toggle @Override
170    public String toString()
171    {
172  0 StringBuilder sb = new StringBuilder();
173  0 sb.append("Title: ").append(title).append("\n");
174  0 sb.append("Chains: ").append(chains).append("\n");
175  0 sb.append("Sequences: ").append(sequences).append("\n");
176  0 sb.append("Base Pairs:\n");
177  0 for (Map.Entry<String, List<BpSpec>> entry : basePairs.entrySet())
178    {
179  0 sb.append(" ").append(entry.getKey()).append(" -> ")
180    .append(entry.getValue()).append("\n");
181    }
182  0 return sb.toString();
183    }
184   
 
185  4 toggle public void generateJalviewSequences(LayeredDBNParser layeredDBNParser)
186    {
187  4 int ch_index = 0;
188  4 for (Chain chain : chains)
189    {
190  8 String chcode = chains.get(ch_index).code,symid=chains.get(ch_index).identifier;
191  8 SequenceI seq = new Sequence(
192    title + "|" + chcode
193  8 + (symid==null ?"":"["+chains.get(ch_index).identifier+"]"),
194    sequences.get(ch_index));
195   
196  8 seq.setStart(chains.get(ch_index).start);
197  8 seq.setEnd(chains.get(ch_index).end);
198   
199  8 boolean first = true;
200  8 layeredDBNParser.addSequence(seq);
201  8 for (String bpType : basePairs.keySet())
202    {
203  48 BpSpec dbn = basePairs.get(bpType).get(ch_index);
204  48 AlignmentAnnotation ssa = StockholmFile.parseAnnotationRow(
205    layeredDBNParser.annotations, "secondary structure",
206    dbn.getWuss());
207  48 ssa.label = "bp:" + bpType;
208  48 seq.addAlignmentAnnotation(ssa);
209  48 ssa.height = 40;
210   
211  48 first = false;
212    }
213  8 ch_index++;
214    }
215    }
216    }
217   
218    // Regex to match the chain definitions: optionally matches "chain" or
219    // "chains", then code, optional [identifier], and (start-end)
220    private static final Pattern CHAIN_PATTERN = Pattern.compile(
221    "(?:chain[s]?\\s+)?([A-Za-z0-9_]+)(?:\\[([^\\]]+)\\])?\\((\\d+)-(\\d+)\\)");
222   
223    // Regex to detect if an entire segment is a list of compact tuples, allowing
224    // optional commas between tuples
225    private static final Pattern COMPACT_LIST_PATTERN = Pattern
226    .compile("^(\\s*,?\\s*\\(\\s*\\d+\\s*,\\s*[^\\s]\\s*\\)\\s*)+$");
227   
228    // Regex to extract the position and bracket character from a single compact
229    // tuple
230    private static final Pattern COMPACT_BP_PATTERN = Pattern
231    .compile("\\(\\s*(\\d+)\\s*,\\s*([^\\s])\\s*\\)");
232   
233    /**
234    * Parses the given structured text into an Extended FASTA Record. Uses
235    * Jalview's FileParse.nextLine() to iterate through the data source.
236    *
237    * @throws IOException
238    * If an error occurs while reading.
239    */
 
240  4 toggle @Override
241    public void parse() throws IOException
242    {
243  4 parsedRecord = new Record();
244   
245  4 String line;
246  ? while ((line = nextLine()) != null)
247    {
248  32 line = line.trim();
249  32 if (line.isEmpty())
250  0 continue;
251   
252    // 1. Parse Header
253  32 if (line.startsWith(">"))
254    {
255    // Split at the first colon
256  4 int colonIndex = line.indexOf(':');
257  4 if (colonIndex != -1)
258    {
259  4 parsedRecord.title = line.substring(1, colonIndex).trim();
260  4 String chainInfo = line.substring(colonIndex + 1);
261   
262    // Extract chains using regex
263  4 Matcher matcher = CHAIN_PATTERN.matcher(chainInfo);
264  12 while (matcher.find())
265    {
266  8 String code = matcher.group(1);
267  8 String identifier = matcher.group(2); // can be null
268  8 int start = Integer.parseInt(matcher.group(3));
269  8 int end = Integer.parseInt(matcher.group(4));
270  8 parsedRecord.chains
271    .add(new Chain(code, identifier, start, end));
272    }
273    }
274    }
275    // 2. Parse Payload (seq and base pair types)
276    else
277    {
278  28 int colonIndex = line.indexOf(':');
279  28 if (colonIndex != -1)
280    {
281  28 String key = line.substring(0, colonIndex).trim();
282    // Get the value, trim leading/trailing whitespace to respect the
283    // coordinate system starting column
284  28 String value = line.substring(colonIndex + 1).trim();
285   
286    // Split by '&' to handle multi-segment payloads
287  28 List<String> segments = Arrays.asList(value.split("&"));
288   
289  28 if ("seq".equalsIgnoreCase(key))
290    {
291  4 parsedRecord.sequences.addAll(segments);
292    }
293    else
294    {
295  24 List<BpSpec> bpSpecs = new ArrayList<>();
296  24 for (String segment : segments)
297    {
298  48 segment = segment.trim();
299    // Determine if the segment is WUSS or a compact list of pairs
300  48 if (COMPACT_LIST_PATTERN.matcher(segment).matches())
301    {
302  0 List<CompactBp> compactList = new ArrayList<>();
303  0 Matcher matcher = COMPACT_BP_PATTERN.matcher(segment);
304  0 while (matcher.find())
305    {
306  0 int pos = Integer.parseInt(matcher.group(1));
307  0 char bracket = matcher.group(2).charAt(0);
308  0 compactList.add(new CompactBp(pos, bracket));
309    }
310  0 bpSpecs.add(new BpSpec(compactList));
311    }
312    else
313    {
314  48 bpSpecs.add(new BpSpec(segment));
315    }
316    }
317  24 parsedRecord.basePairs.put(key, bpSpecs);
318    }
319    }
320    }
321    }
322    // Process into annotated sequences
323  4 parsedRecord.generateJalviewSequences(this);
324   
325    }
326   
 
327  1 toggle public Record getRecord()
328    {
329  1 return parsedRecord;
330    }
331   
 
332  0 toggle @Override
333    public String print(SequenceI[] seqs, boolean jvsuffix)
334    {
335    // TODO Auto-generated method stub
336  0 return null;
337    }
338    }