/*
* Copyright (C) 2012.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* This program 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.
*/
package uk.me.parabola.imgfmt.app.mdr;
import java.nio.charset.Charset;
import java.text.Collator;
import java.util.ArrayList;
import java.util.List;
import uk.me.parabola.imgfmt.app.ImgFileWriter;
import uk.me.parabola.imgfmt.app.srt.Sort;
/**
* Holds an index of name prefixes to record numbers.
*
* Extends MdrSection, although is sometimes a subsection, not an actual section.
*
* @author Steve Ratcliffe
*/
public class PrefixIndex extends MdrSection {
private final int prefixLength;
private int maxIndex;
// We use mdr8record for all similar indexes.
private final List<Mdr8Record> index = new ArrayList<>();
/**
* Sets the config and the prefix length for this index.
*
* Prefix length may differ depending on the amount of data, so will have
* to deal with that when it happens.
*
* @param config Configuration for sorting methods.
* @param prefixLength The prefix length for this index.
*/
public PrefixIndex(MdrConfig config, int prefixLength) {
setConfig(config);
this.prefixLength = prefixLength;
}
/**
* We can create an index for any type that has a name.
* @param list A list of items that have a name.
*/
public void createFromList(List<? extends NamedRecord> list, boolean grouped) {
maxIndex = list.size();
// Prefixes are equal based on the primary unaccented character, so
// we need to use the collator to test for equality and not equals().
Sort sort = getConfig().getSort();
Collator collator = sort.getCollator();
collator.setStrength(Collator.PRIMARY);
String lastCountryName = null;
String lastPrefix = "";
int inRecord = 0; // record number of the input list
int outRecord = 0; // record number of the index
for (NamedRecord r : list) {
inRecord++;
String prefix = getPrefix(r.getName());
if (collator.compare(prefix, lastPrefix) != 0) {
outRecord++;
Mdr8Record ind = new Mdr8Record();
ind.setPrefix(prefix);
ind.setRecordNumber(inRecord);
index.add(ind);
lastPrefix = prefix;
if (grouped) {
// Peek into the real type to support the mdr17 feature of indexes sorted on country.
Mdr5Record city = ((Mdr7Record) r).getCity();
if (city != null) {
String countryName = city.getCountryName();
if (!countryName.equals(lastCountryName)) {
city.getMdrCountry().getMdr29().setMdr17(outRecord);
lastCountryName = countryName;
}
}
}
}
}
}
public void createFromList(List<? extends NamedRecord> list) {
createFromList(list, false);
}
/**
* Write the section or subsection.
*/
public void writeSectData(ImgFileWriter writer) {
int size = numberToPointerSize(maxIndex);
Charset charset = getConfig().getSort().getCharset();
for (Mdr8Record s : index) {
writer.put(s.getPrefix().getBytes(charset), 0, prefixLength);
putN(writer, size, s.getRecordNumber());
}
}
public int getItemSize() {
return prefixLength + numberToPointerSize(maxIndex);
}
protected int numberOfItems() {
return index.size();
}
/**
* Get the prefix of the name at the given record.
* If the name is shorter than the prefix length, then it padded with nul characters.
* So it can be longer than the input string.
*
* @param in The name to truncate.
* @return A string prefixLength characters long, consisting of the initial
* prefix of name and padded with nulls if necessary to make up the length.
*/
private String getPrefix(String in) {
StringBuilder sb = new StringBuilder();
char[] chars = in.toCharArray();
int ci = 0;
for (int i = 0; i < prefixLength; i++) {
char c = 0;
while (ci < chars.length) {
// TODO: simplify when initial spaces are removed
c = chars[ci++];
if (ci == 1 && c== 0x20)
continue;
if (c >= 0x20)
break;
}
sb.append(c);
}
return sb.toString();
}
public int getPrefixLength() {
return prefixLength;
}
}