/*
* -----------------------------------------------------------------------
* Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/>
* -----------------------------------------------------------------------
* This file (TimezoneIDProcessor.java) is part of project Time4J.
*
* Time4J is free software: You can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* Time4J 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Time4J. If not, see <http://www.gnu.org/licenses/>.
* -----------------------------------------------------------------------
*/
package net.time4j.format.expert;
import net.time4j.engine.AttributeQuery;
import net.time4j.engine.ChronoDisplay;
import net.time4j.engine.ChronoElement;
import net.time4j.tz.TZID;
import net.time4j.tz.Timezone;
import net.time4j.tz.ZonalOffset;
import java.io.IOException;
import java.util.List;
import java.util.Set;
/**
* <p>Verarbeitet eine Zeitzonen-ID. </p>
*
* @author Meno Hochschild
* @since 3.0
*/
enum TimezoneIDProcessor
implements FormatProcessor<TZID> {
//~ Statische Felder/Initialisierungen --------------------------------
/** Singleton. */
INSTANCE;
//~ Methoden ----------------------------------------------------------
@Override
public void print(
ChronoDisplay formattable,
Appendable buffer,
AttributeQuery attributes,
Set<ElementPosition> positions,
boolean quickPath
) throws IOException {
if (!formattable.hasTimezone()) {
throw new IllegalArgumentException(
"Cannot extract timezone id from: " + formattable);
}
int start = -1;
int printed;
if (buffer instanceof CharSequence) {
start = ((CharSequence) buffer).length();
}
String canonical = formattable.getTimezone().canonical();
buffer.append(canonical);
printed = canonical.length();
if (
(start != -1)
&& (printed > 0)
&& (positions != null)
) {
positions.add(
new ElementPosition(
TimezoneElement.TIMEZONE_ID,
start,
start + printed));
}
}
@Override
public void parse(
CharSequence text,
ParseLog status,
AttributeQuery attributes,
ParsedEntity<?> parsedResult,
boolean quickPath
) {
int len = text.length();
int start = status.getPosition();
int pos = start;
if (pos >= len) {
status.setError(start, "Missing timezone name.");
return;
}
// Zeitzonen-ID einlesen
StringBuilder name = new StringBuilder();
while (pos < len) {
char c = text.charAt(pos);
if ( // siehe Theory-Datei in TZDB
(c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c == '-')
|| (c == '_')
|| (c == '/')
) {
name.append(c);
pos++;
} else {
break;
}
}
if (!Character.isLetter(name.charAt(name.length() - 1))) {
name.deleteCharAt(name.length() - 1);
pos--;
}
String key = name.toString();
// Offset prüfen
if (key.isEmpty()) {
status.setError(start, "Missing valid timezone id.");
return;
} else if (key.startsWith("Etc/GMT")) {
status.setError(
start,
"Inverse Etc/GMT-Offsets are not supported, "
+ "use UTC-Offsets instead.");
return;
} else if (key.equals("Z")) {
parsedResult.put(TimezoneElement.TIMEZONE_OFFSET, ZonalOffset.UTC);
status.setPosition(pos);
return;
} else if (
key.equals("UTC")
|| key.equals("GMT")
|| key.equals("UT")
) {
if (len > pos) {
char c = text.charAt(pos);
if ((c == '+') || (c == '-')) {
status.setPosition(pos);
TimezoneOffsetProcessor.EXTENDED_LONG_PARSER.parse(
text, status, attributes, parsedResult, quickPath);
return;
}
}
parsedResult.put(TimezoneElement.TIMEZONE_OFFSET, ZonalOffset.UTC);
status.setPosition(pos);
return;
}
// binäre Suche
List<TZID> zones = Timezone.getAvailableIDs("INCLUDE_ALIAS");
int low = 0;
int high = zones.size() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
TZID zone = zones.get(mid);
int cmp = zone.canonical().compareTo(key);
if (cmp < 0) {
low = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
parsedResult.put(TimezoneElement.TIMEZONE_ID, zone);
status.setPosition(pos);
return;
}
}
status.setError(start, "Cannot parse to timezone id: " + key);
}
@Override
public ChronoElement<TZID> getElement() {
return TimezoneElement.TIMEZONE_ID;
}
@Override
public FormatProcessor<TZID> withElement(ChronoElement<TZID> element) {
return INSTANCE;
}
@Override
public boolean isNumerical() {
return false;
}
@Override
public FormatProcessor<TZID> quickPath(
ChronoFormatter<?> formatter,
AttributeQuery attributes,
int reserved
) {
return INSTANCE;
}
}