001: /*
002: * This file is part of JGAP.
003: *
004: * JGAP offers a dual license model containing the LGPL as well as the MPL.
005: *
006: * For licensing information please see the file license.txt included with JGAP
007: * or have a look at the top of class org.jgap.Chromosome which representatively
008: * includes the JGAP license policy applicable for any file delivered with JGAP.
009: */
010: package org.jgap;
011:
012: import java.io.*;
013: import java.lang.reflect.*;
014: import java.util.*;
015:
016: import org.jgap.util.*;
017:
018: /**
019: * List of chromosomes held in the Genotype (or possibly later in the
020: * Configuration object).
021: *
022: * @author Klaus Meffert
023: * @since 2.0
024: */
025: public class Population implements Serializable, ICloneable,
026: IPersistentRepresentation {
027: /** String containing the CVS revision. Read out via reflection!*/
028: private static final String CVS_REVISION = "$Revision: 1.63 $";
029:
030: /**
031: * The array of Chromosomes that makeup the Genotype's population.
032: */
033: private List m_chromosomes;
034:
035: /**
036: * The fittest Chromosome of the population.
037: */
038: private IChromosome m_fittestChromosome;
039:
040: /**
041: * Indicates whether at least one of the chromosomes has been changed
042: * (deleted, added, modified).
043: */
044: private boolean m_changed;
045:
046: /**
047: * Indicates that the list of Chromosomes has been sorted.
048: */
049: private boolean m_sorted;
050:
051: private Configuration m_config;
052:
053: public final static String CHROM_DELIMITER = "~";
054:
055: /**
056: * Represents the heading delimiter that is used to separate chromosomes in
057: * the persistent representation.
058: */
059: public final static String CHROM_DELIMITER_HEADING = "[";
060:
061: /**
062: * Represents the closing delimiter that is used to separate chromosomes in
063: * the persistent representation.
064: */
065: public final static String CHROM_DELIMITER_CLOSING = "]";
066:
067: /*
068: *
069: * @author Klaus Meffert
070: * @since 3.0
071: */
072: public Population(final Configuration a_config)
073: throws InvalidConfigurationException {
074: this (a_config, 100);
075: }
076:
077: /*
078: * Constructs the Population from a list of Chromosomes. Does not use cloning!
079: *
080: * @param a_chromosomes the Chromosome's to be used for building the
081: * Population
082: *
083: * @author Klaus Meffert
084: * @since 2.0
085: */
086: public Population(final Configuration a_config,
087: final IChromosome[] a_chromosomes)
088: throws InvalidConfigurationException {
089: this (a_config, a_chromosomes.length);
090: synchronized (m_chromosomes) {
091: for (int i = 0; i < a_chromosomes.length; i++) {
092: // In here we could test for null chromosomes, but this is skipped
093: // because of performance issues (although this may seem idiotic...)
094: m_chromosomes.add(a_chromosomes[i]);
095: }
096: }
097: setChanged(true);
098: }
099:
100: /*
101: * Constructs the Population from a single Chromosome. Does not use cloning!
102:
103: * @param a_chromosome the Chromosome to be used for building the Population
104: *
105: * @author Klaus Meffert
106: * @since 3.2
107: */
108: public Population(final Configuration a_config,
109: final IChromosome a_chromosome)
110: throws InvalidConfigurationException {
111: this (a_config, 1);
112: if (a_chromosome == null) {
113: throw new IllegalArgumentException(
114: "Chromosome passed must not be null!");
115: }
116: synchronized (m_chromosomes) {
117: m_chromosomes.add(a_chromosome);
118: }
119: setChanged(true);
120: }
121:
122: /*
123: * Constructs an empty Population with the given initial size.
124:
125: * @param a_size the initial size of the empty Population. The initial size
126: * is not fix, it is just for optimized list creation.
127: *
128: * @author Klaus Meffert
129: * @since 2.0
130: */
131: public Population(final Configuration a_config, final int a_size)
132: throws InvalidConfigurationException {
133: if (a_config == null) {
134: throw new InvalidConfigurationException(
135: "Configuration must not be null!");
136: }
137: m_config = a_config;
138: // Use a synchronized list (important for distributed computing!)
139: m_chromosomes = new Vector(a_size);
140: setChanged(true);
141: }
142:
143: /*
144: * Constructs an empty Population with initial array size 100.
145: *
146: * @author Klaus Meffert
147: * @since 2.0
148: */
149: public Population() throws InvalidConfigurationException {
150: this (Genotype.getStaticConfiguration());
151: }
152:
153: public Configuration getConfiguration() {
154: return m_config;
155: }
156:
157: /**
158: * Adds a Chromosome to this Population. Does nothing when given null.
159: *
160: * @param a_toAdd the Chromosome to add
161: *
162: * @author Klaus Meffert
163: * @since 2.0
164: */
165: public void addChromosome(final IChromosome a_toAdd) {
166: if (a_toAdd != null) {
167: synchronized (m_chromosomes) {
168: m_chromosomes.add(a_toAdd);
169: }
170: setChanged(true);
171: }
172: }
173:
174: /**
175: * Adds all the Chromosomes in the given Population. Does nothing on null or
176: * an empty Population.
177: *
178: * @param a_population the Population to add
179: *
180: * @author Klaus Meffert
181: * @since 2.0
182: */
183: public void addChromosomes(final Population a_population) {
184: if (a_population != null) {
185: synchronized (m_chromosomes) {
186: m_chromosomes.addAll(a_population.getChromosomes());
187: }
188: // The following would do the same:
189: // if (a_population.getChromosomes() != null) {
190: // int size = a_population.getChromosomes().size();
191: // for (int i = 0; i < size; i++) {
192: // IChromosome chrom = a_population.getChromosome(i);
193: // m_chromosomes.add(chrom);
194: // }
195: // }
196: setChanged(true);
197: }
198: }
199:
200: /**
201: * Replaces all chromosomes in the population with the give list of
202: * chromosomes.
203: *
204: * @param a_chromosomes the chromosomes to make the population up from
205: *
206: * @author Klaus Meffert
207: */
208: public void setChromosomes(final List a_chromosomes) {
209: synchronized (m_chromosomes) {
210: m_chromosomes = a_chromosomes;
211: }
212: setChanged(true);
213: }
214:
215: /**
216: * Sets in the given Chromosome on the given index in the list of chromosomes.
217: * If the given index is exceeding the list by one, the chromosome is
218: * appended.
219: *
220: * @param a_index the index to set the Chromosome in
221: * @param a_chromosome the Chromosome to be set
222: *
223: * @author Klaus Meffert
224: * @since 2.0
225: */
226: public void setChromosome(final int a_index,
227: final IChromosome a_chromosome) {
228: if (m_chromosomes.size() == a_index) {
229: addChromosome(a_chromosome);
230: } else {
231: synchronized (m_chromosomes) {
232: m_chromosomes.set(a_index, a_chromosome);
233: }
234: setChanged(true);
235: }
236: }
237:
238: /**
239: * @return the list of Chromosome's in the Population. Don't modify the
240: * retrieved list by using clear(), remove(int) etc. If you do so, you need to
241: * call setChanged(true)
242: *
243: * @author Klaus Meffert
244: * @since 2.0
245: */
246: public List getChromosomes() {
247: return m_chromosomes;
248: }
249:
250: /**
251: * @param a_index the index of the Chromosome to be returned
252: * @return Chromosome at given index in the Population
253: *
254: * @author Klaus Meffert
255: * @since 2.0
256: */
257: public IChromosome getChromosome(final int a_index) {
258: return (IChromosome) m_chromosomes.get(a_index);
259: }
260:
261: /**
262: * @return number of Chromosome's in the Population
263: *
264: * @author Klaus Meffert
265: * @since 2.0
266: */
267: public int size() {
268: return m_chromosomes.size();
269: }
270:
271: /**
272: * @return Iterator for the Chromosome list in the Population. Please be aware
273: * that using remove() forces you to call setChanged(true)
274: *
275: * @author Klaus Meffert
276: * @since 2.0
277: */
278: public Iterator iterator() {
279: return m_chromosomes.iterator();
280: }
281:
282: /**
283: * @return the Population converted into a list of Chromosome's
284: *
285: * @author Klaus Meffert, Dan Clark
286: * @since 2.0
287: */
288: public IChromosome[] toChromosomes() {
289: return (IChromosome[]) m_chromosomes
290: .toArray(new IChromosome[m_chromosomes.size()]);
291: }
292:
293: /**
294: * Determines the fittest Chromosome in the Population (the one with the
295: * highest fitness value) and memorizes it. This is an optimized version
296: * compared to calling determineFittesChromosomes(1).
297: *
298: * @return the fittest Chromosome of the Population
299: *
300: * @author Klaus Meffert
301: * @since 2.0
302: */
303: public IChromosome determineFittestChromosome() {
304: if (!m_changed && m_fittestChromosome != null) {
305: return m_fittestChromosome;
306: }
307: Iterator it = m_chromosomes.iterator();
308: FitnessEvaluator evaluator = getConfiguration()
309: .getFitnessEvaluator();
310: double bestFitness;
311: if (evaluator.isFitter(2.0d, 1.0d)) {
312: bestFitness = -1.0d;
313: } else {
314: bestFitness = Double.MAX_VALUE;
315: }
316: double fitness;
317: while (it.hasNext()) {
318: IChromosome chrom = (IChromosome) it.next();
319: fitness = chrom.getFitnessValue();
320: if (evaluator.isFitter(fitness, bestFitness)
321: || m_fittestChromosome == null) {
322: m_fittestChromosome = chrom;
323: bestFitness = fitness;
324: }
325: }
326: setChanged(false);
327: return m_fittestChromosome;
328: }
329:
330: /**
331: * Determines the fittest Chromosome in the population (the one with the
332: * highest fitness value) within the given indices and memorizes it. This is
333: * an optimized version compared to calling determineFittesChromosomes(1).
334: *
335: * @param a_startIndex index to begin the evaluation with
336: * @param a_endIndex index to end the evaluation with
337: * @return the fittest Chromosome of the population within the given indices
338: *
339: * @author Klaus Meffert
340: * @since 3.0
341: */
342: public IChromosome determineFittestChromosome(int a_startIndex,
343: int a_endIndex) {
344: double bestFitness = -1.0d;
345: FitnessEvaluator evaluator = getConfiguration()
346: .getFitnessEvaluator();
347: double fitness;
348: int startIndex = Math.max(0, a_startIndex);
349: int endIndex = Math.min(m_chromosomes.size() - 1, a_endIndex);
350: m_fittestChromosome = null;
351: for (int i = startIndex; i <= endIndex; i++) {
352: IChromosome chrom = (IChromosome) m_chromosomes.get(i);
353: fitness = chrom.getFitnessValue();
354: if (evaluator.isFitter(fitness, bestFitness)
355: || m_fittestChromosome == null) {
356: m_fittestChromosome = chrom;
357: bestFitness = fitness;
358: }
359: }
360: return m_fittestChromosome;
361: }
362:
363: /**
364: * Mark that for the population the fittest chromosome may have changed.
365: *
366: * @param a_changed true: population's fittest chromosome may have changed,
367: * false: fittest chromosome evaluated earlier is still valid
368: *
369: * @author Klaus Meffert
370: * @since 2.2
371: */
372: protected void setChanged(final boolean a_changed) {
373: m_changed = a_changed;
374: setSorted(false);
375: }
376:
377: /**
378: * @return true: population's chromosomes (maybe) were changed,
379: * false: not changed for sure
380: *
381: * @since 2.6
382: */
383: public boolean isChanged() {
384: return m_changed;
385: }
386:
387: /**
388: * Mark the population as sorted.
389: *
390: * @param a_sorted true: mark population as sorted
391: *
392: * @author Klaus Meffert
393: * @since 2.6
394: */
395: protected void setSorted(final boolean a_sorted) {
396: m_sorted = a_sorted;
397: }
398:
399: /**
400: * Determines whether the given chromosome is contained within the population.
401: * @param a_chromosome the chromosome to check
402: * @return true: chromosome contained within population
403: *
404: * @author Klaus Meffert
405: * @since 2.1
406: */
407: public boolean contains(final IChromosome a_chromosome) {
408: return m_chromosomes.contains(a_chromosome);
409: }
410:
411: /**
412: * Removes a chromosome in the list at the given index. Method has package
413: * visibility to signal that this is a method not to be used outside the
414: * JGAP kernel under normal circumstances.
415: *
416: * @param a_index index of chromosome to be removed in list
417: * @return removed Chromosome
418: *
419: * @author Klaus Meffert
420: * @since 2.4
421: */
422: IChromosome removeChromosome(final int a_index) {
423: if (a_index < 0 || a_index >= size()) {
424: throw new IllegalArgumentException(
425: "Index must be within bounds!");
426: }
427: setChanged(true);
428: return (IChromosome) m_chromosomes.remove(a_index);
429: }
430:
431: /**
432: * Cares that the population size does not exceed the maximum size given in
433: * the configuration.
434: *
435: * @throws InvalidConfigurationException
436: *
437: * @author Klaus Meffert
438: * @since 3.2
439: */
440: public void keepPopSizeConstant()
441: throws InvalidConfigurationException {
442: int popSize = size();
443: // See request 1213752.
444: // ---------------------
445: int maxSize = getConfiguration().getPopulationSize();
446: // INaturalSelector selector = getConfiguration().getKeepPopConstantSelector();
447: // if (popSize > maxSize) {
448: // Population newPop = new Population(getConfiguration(), maxSize);
449: // selector.select(maxSize, this, newPop);
450: // m_chromosomes = newPop.getChromosomes();
451: // setChanged(true);
452: // }
453: while (popSize > maxSize) {
454: // Remove a chromosome.
455: // --------------------
456: /**@todo use dedicated selector for that*/
457: removeChromosome(0);
458: popSize--;
459: }
460: }
461:
462: /**
463: * Sorts the Chromosome list and returns the fittest n Chromosomes in
464: * the population.
465: *
466: * @param a_numberOfChromosomes number of top performer chromosomes to be
467: * returned
468: * @return list of the fittest n Chromosomes of the population, or the fittest
469: * x Chromosomes with x = number of chromosomes in case n > x.
470: *
471: * @author Charles Kevin Hill
472: * @since 2.4
473: */
474: public List determineFittestChromosomes(
475: final int a_numberOfChromosomes) {
476: int numberOfChromosomes = Math.min(a_numberOfChromosomes,
477: getChromosomes().size());
478: if (numberOfChromosomes <= 0) {
479: return null;
480: }
481: if (!m_changed && m_sorted) {
482: return getChromosomes().subList(0, numberOfChromosomes);
483: }
484: // Sort the list of chromosomes using the fitness comparator
485: sortByFitness();
486: // Return the top n chromosomes
487: return getChromosomes().subList(0, numberOfChromosomes);
488: }
489:
490: /**
491: * Sorts the chromosomes within the population according to their fitness
492: * value using ChromosomFitnessComparator. The fittest chromosome is then
493: * at index 0.
494: *
495: * @author Klaus Meffert
496: * @since 2.6
497: */
498: public void sortByFitness() {
499: // The following construction could be cached but wrt that the
500: // evaluator registered with the configuration could change
501: // --> Don't cache it!
502: sort(new ChromosomeFitnessComparator(getConfiguration()
503: .getFitnessEvaluator()));
504: setChanged(false);
505: setSorted(true);
506: m_fittestChromosome = (IChromosome) m_chromosomes.get(0);
507: }
508:
509: /**
510: * Sorts the chromosomes within the population utilzing the given comparator.
511: *
512: * @param a_comparator the comparator to utilize for sorting
513: *
514: * @author Klaus Meffert
515: * @since 2.6
516: */
517: protected void sort(Comparator a_comparator) {
518: Collections.sort(getChromosomes(), a_comparator);
519: }
520:
521: /**
522: * Returns the genotype of the population, i.e. the list of genes in the
523: * Population.
524: *
525: * @param a_resolveCompositeGenes true: split encountered CompositeGenes
526: * into their single (atomic) genes
527: * @return genotype of the population
528: *
529: * @author Klaus Meffert
530: * @since 2.3
531: */
532: public List getGenome(final boolean a_resolveCompositeGenes) {
533: List result = new Vector();
534: List chroms = getChromosomes();
535: int len = chroms.size();
536: for (int i = 0; i < len; i++) {
537: IChromosome chrom = (IChromosome) chroms.get(i);
538: Gene[] genes = chrom.getGenes();
539: int len2 = genes.length;
540: for (int j = 0; j < len2; j++) {
541: Gene gene = genes[j];
542: if (a_resolveCompositeGenes
543: && gene instanceof ICompositeGene) {
544: addCompositeGene(result, (ICompositeGene) gene);
545: } else {
546: addAtomicGene(result, gene);
547: }
548: }
549: }
550: return result;
551: }
552:
553: /**
554: * Adds all the genes of a CompositeGene to a result list.<p>
555: * Note: Method calls itself recursively.
556: *
557: * @param a_result the list to add to
558: * @param a_gene the gene to start with
559: *
560: * @author Klaus Meffert
561: * @since 2.3
562: */
563: private void addCompositeGene(final List a_result, final Gene a_gene) {
564: if (a_gene instanceof ICompositeGene) {
565: int len = a_gene.size();
566: for (int i = 0; i < len; i++) {
567: addCompositeGene(a_result, ((ICompositeGene) a_gene)
568: .geneAt(i));
569: }
570: } else {
571: addAtomicGene(a_result, a_gene);
572: }
573: }
574:
575: /**
576: * Helper method for addCompositeGene.
577: *
578: * @param a_result List
579: * @param a_gene Gene
580: *
581: * @author Klaus Meffert
582: * @since 2.3
583: */
584: private void addAtomicGene(final List a_result, final Gene a_gene) {
585: a_result.add(a_gene);
586: }
587:
588: public boolean isSorted() {
589: return m_sorted;
590: }
591:
592: /**
593: * The equals-method.
594: *
595: * @param a_pop the population instance to compare with
596: * @return true: given object equal to comparing one
597: *
598: * @author Klaus Meffert
599: * @since 2.6
600: */
601: public boolean equals(Object a_pop) {
602: try {
603: return compareTo(a_pop) == 0;
604: } catch (ClassCastException e) {
605: // If the other object isn't an Population instance
606: // then we're not equal.
607: // ------------------------------------------------
608: return false;
609: }
610: }
611:
612: /**
613: * This method is not producing symmetric results as -1 is more often returned
614: * than 1 (see description of return value).
615: *
616: * @param a_pop the other population to compare
617: * @return 1: a_pop is null or having fewer chromosomes or equal number
618: * of chromosomes but at least one not contained. 0: both populations
619: * containing exactly the same chromosomes. -1: this population contains fewer
620: * chromosomes than a_pop
621: *
622: * @author Klaus Meffert
623: * @since 2.6
624: */
625: public int compareTo(Object a_pop) {
626: Population other = (Population) a_pop;
627: if (a_pop == null) {
628: return 1;
629: }
630: int size1 = size();
631: int size2 = other.size();
632: if (size1 != size2) {
633: if (size1 < size2) {
634: return -1;
635: } else {
636: return 1;
637: }
638: }
639: List chroms2 = other.getChromosomes();
640: for (int i = 0; i < size1; i++) {
641: if (!chroms2.contains(m_chromosomes.get(i))) {
642: return 1;
643: }
644: }
645: return 0;
646: }
647:
648: /**
649: * @return deeply cloned population instance
650: *
651: * @author Klaus Meffert
652: * @since 3.2
653: */
654: public Object clone() {
655: try {
656: Population result = new Population(m_config);
657: // Precautiously set changed to true in case cloning is not 1:1
658: // ------------------------------------------------------------
659: result.m_changed = true;
660: result.m_sorted = false;
661: result.m_fittestChromosome = m_fittestChromosome;
662: int size = m_chromosomes.size();
663: for (int i = 0; i < size; i++) {
664: IChromosome chrom = (IChromosome) m_chromosomes.get(i);
665: result.addChromosome((IChromosome) chrom.clone());
666: }
667: return result;
668: } catch (Exception ex) {
669: throw new CloneException(ex);
670: }
671: }
672:
673: /**
674: * Clears the list of chromosomes. Normally, this should not be necessary.
675: * But especially in distributed computing, a fresh population has to be
676: * provided sometimes.
677: *
678: * @author Klaus Meffert
679: * @since 3.2
680: */
681: public void clear() {
682: m_chromosomes.clear();
683: m_changed = true;
684: m_sorted = true;
685: m_fittestChromosome = null;
686: }
687:
688: /**
689: * Returns a persistent representation of this chromosome, see interface Gene
690: * for description. Similar to CompositeGene's routine. But does not include
691: * all information of the chromosome (yet).
692: *
693: * @return string representation of this Chromosome's relevant parts of its
694: * current state
695: * @throws UnsupportedOperationException
696: *
697: * @author Klaus Meffert
698: * @since 3.2
699: */
700: public String getPersistentRepresentation() {
701: StringBuffer b = new StringBuffer();
702: // Persist the chromosomes.
703: // ------------------------
704: IChromosome chrom;
705: for (int i = 0; i < m_chromosomes.size(); i++) {
706: chrom = (IChromosome) m_chromosomes.get(i);
707: if (!(chrom instanceof IPersistentRepresentation)) {
708: throw new RuntimeException(
709: "Population contains a chromosome of type "
710: + chrom.getClass().getName()
711: + " which does not implement"
712: + " IPersistentRepresentation!");
713: }
714: b.append(CHROM_DELIMITER_HEADING);
715: b.append(StringKit.encode(chrom.getClass().getName()
716: + CHROM_DELIMITER
717: + ((IPersistentRepresentation) chrom)
718: .getPersistentRepresentation()));
719: b.append(CHROM_DELIMITER_CLOSING);
720: }
721: return b.toString();
722: }
723:
724: /**
725: * Counterpart of getPersistentRepresentation.
726: *
727: * @param a_representation the string representation retrieved from a prior
728: * call to the getPersistentRepresentation() method
729: *
730: * @throws UnsupportedRepresentationException
731: *
732: * @author Klaus Meffert
733: * @since 3.2
734: */
735: public void setValueFromPersistentRepresentation(
736: String a_representation)
737: throws UnsupportedRepresentationException {
738: if (a_representation != null) {
739: try {
740: List r = split(a_representation);
741: String g;
742: m_chromosomes = new Vector();
743: // Obtain the chromosomes.
744: // -----------------------
745: Iterator iter = r.iterator();
746: StringTokenizer st;
747: String clas;
748: String representation;
749: IChromosome chrom;
750: while (iter.hasNext()) {
751: g = StringKit.decode((String) iter.next());
752: st = new StringTokenizer(g, CHROM_DELIMITER);
753: if (st.countTokens() != 2)
754: throw new UnsupportedRepresentationException(
755: "In "
756: + g
757: + ", "
758: + "expecting two tokens, separated by "
759: + CHROM_DELIMITER);
760: clas = st.nextToken();
761: representation = st.nextToken();
762: chrom = createChromosome(clas, representation);
763: m_chromosomes.add(chrom);
764: }
765: setChanged(true);
766: } catch (Exception ex) {
767: throw new UnsupportedRepresentationException(ex
768: .toString());
769: }
770: }
771: }
772:
773: /**
774: * Creates a new Chromosome instance.<p>
775: * Taken and adapted from CompositeGene.
776: *
777: * @param a_chromClassName name of the Chromosome class
778: * @param a_persistentRepresentation persistent representation of the
779: * Chromosome to create (could be obtained via getPersistentRepresentation)
780: *
781: * @return newly created Chromosome
782: * @throws Exception
783: *
784: * @author Klaus Meffert
785: * @since 3.2
786: */
787: protected IChromosome createChromosome(String a_chromClassName,
788: String a_persistentRepresentation) throws Exception {
789: Class chromClass = Class.forName(a_chromClassName);
790: Constructor constr = chromClass
791: .getConstructor(new Class[] { Configuration.class });
792: IChromosome chrom = (IChromosome) constr
793: .newInstance(new Object[] { getConfiguration() });
794: ((IPersistentRepresentation) chrom)
795: .setValueFromPersistentRepresentation(a_persistentRepresentation);
796: return chrom;
797: }
798:
799: /**
800: * Splits the input a_string into individual chromosome representations.<p>
801: * Taken and adapted from CompositeGene.
802: *
803: * @param a_string the string to split
804: * @return the elements of the returned array are the persistent
805: * representation strings of the population's components
806: * @throws UnsupportedRepresentationException
807: *
808: * @author Klaus Meffert
809: * @since 3.2
810: */
811: protected static final List split(String a_string)
812: throws UnsupportedRepresentationException {
813: List a = Collections.synchronizedList(new ArrayList());
814: // No Header data.
815: // ---------------
816:
817: // Chromosome data.
818: // ----------------
819: StringTokenizer st = new StringTokenizer(a_string,
820: CHROM_DELIMITER_HEADING + CHROM_DELIMITER_CLOSING, true);
821: while (st.hasMoreTokens()) {
822: if (!st.nextToken().equals(CHROM_DELIMITER_HEADING)) {
823: throw new UnsupportedRepresentationException(a_string
824: + " no open tag");
825: }
826: String n = st.nextToken();
827: if (n.equals(CHROM_DELIMITER_CLOSING)) {
828: a.add(""); /* Empty token */
829: } else {
830: a.add(n);
831: if (!st.nextToken().equals(CHROM_DELIMITER_CLOSING)) {
832: throw new UnsupportedRepresentationException(
833: a_string + " no close tag");
834: }
835: }
836: }
837: return a;
838: }
839: }
|