All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.time4j.engine.Chronology Maven / Gradle / Ivy

There is a newer version: 4.38
Show newest version
/*
 * -----------------------------------------------------------------------
 * Copyright © 2013-2015 Meno Hochschild, 
 * -----------------------------------------------------------------------
 * This file (Chronology.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 .
 * -----------------------------------------------------------------------
 */

package net.time4j.engine;

import net.time4j.base.TimeSource;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;


/**
 * 

Represents a system of chronological elements which form any kind * of temporal value.

* * @param generic type compatible to {@link ChronoEntity} * @author Meno Hochschild */ /*[deutsch] *

Repräsentiert ein System von chronologischen Elementen, die * zusammen einen zeitlichen Wert formen.

* * @param generic type compatible to {@link ChronoEntity} * @author Meno Hochschild */ public class Chronology> implements ChronoMerger { //~ Statische Felder/Initialisierungen -------------------------------- private static final List CHRONOS = new CopyOnWriteArrayList(); private static final ReferenceQueue> QUEUE = new ReferenceQueue>(); //~ Instanzvariablen -------------------------------------------------- private final Class chronoType; private final ChronoMerger merger; private final Map, ElementRule> ruleMap; private final List extensions; //~ Konstruktoren ----------------------------------------------------- /** *

Standard-Konstruktor.

* *

Implementierungshinweis: Subklassen sollten grundsätzlich * dem Singleton-Muster folgen und deshalb keinen öffentlichen * Konstruktor bieten.

* * @param chronoType chronological type * @param chronoMerger creates a new instance of T based on infos * from another source * @param ruleMap registered elements and rules * @param extensions optional extensions */ Chronology( Class chronoType, ChronoMerger chronoMerger, Map, ElementRule> ruleMap, List extensions ) { super(); if (chronoType == null) { throw new NullPointerException("Missing chronological type."); } else if (chronoMerger == null) { throw new NullPointerException("Missing chronological merger."); } this.chronoType = chronoType; this.merger = chronoMerger; this.ruleMap = Collections.unmodifiableMap(ruleMap); this.extensions = Collections.unmodifiableList(extensions); } //~ Methoden ---------------------------------------------------------- /** *

Returns the chronological type.

* * @return type of time context */ /*[deutsch] *

Liefert den chronologischen Typ.

* * @return type of time context */ public Class getChronoType() { return this.chronoType; } /** *

Returns all registered chronological elements.

* * @return unmodifiable set of elements without duplicates */ /*[deutsch] *

Liefert den zu dieser Chronologie zugehörigen Satz * von registrierten chronologischen Elementen.

* * @return unmodifiable set of elements without duplicates */ public Set> getRegisteredElements() { return this.ruleMap.keySet(); } /** *

Queries if given chronological element is registered together * with its element rule.

* * @param element element to be asked (optional) * @return {@code true} if registered else {@code false} */ /*[deutsch] *

Ist das angegebene chronologische Element inklusive Regel * registriert?

* * @param element element to be asked (optional) * @return {@code true} if registered else {@code false} */ public boolean isRegistered(ChronoElement element) { return ((element != null) && this.ruleMap.containsKey(element)); } /** *

Queries if given chronological element is supported by this * chronology.

* *

The element will be supported if it is either registered or * defines a suitable element rule for this chronology.

* * @param element element to be asked (optional) * @return {@code true} if supported else {@code false} */ /*[deutsch] *

Wird das angegebene chronologische Element unterstützt?

* *

Unterstützung ist gegeben, wenn das Element entweder registriert * ist oder eine zu dieser Chronologie passende Regel definiert.

* * @param element element to be asked (optional) * @return {@code true} if supported else {@code false} */ public boolean isSupported(ChronoElement element) { if (element == null) { return false; } else { return ( this.isRegistered(element) || (this.getDerivedRule(element, false) != null) ); } } @Override public T createFrom( TimeSource clock, AttributeQuery attributes ) { if (attributes == null) { throw new NullPointerException("Missing attributes."); } return this.merger.createFrom(clock, attributes); } @Override public T createFrom( ChronoEntity entity, AttributeQuery attributes, boolean preparsing ) { if (attributes == null) { throw new NullPointerException("Missing attributes."); } return this.merger.createFrom(entity, attributes, preparsing); } @Override public ChronoDisplay preformat( T context, AttributeQuery attributes ) { return this.merger.preformat(context, attributes); } @Override public Chronology preparser() { return this.merger.preparser(); } @Override public String getFormatPattern( DisplayStyle style, Locale locale ) { return this.merger.getFormatPattern(style, locale); } @Override public StartOfDay getDefaultStartOfDay() { return this.merger.getDefaultStartOfDay(); } /** *

Returns all registered chronological extensions.

* *

This method will be called by format-API in order to collect * all extension elements which are relevant for formatting.

* * @return unmodifiable list of extensions */ /*[deutsch] *

Liefert die registrierten chronologischen Erweiterungen.

* *

Diese Methode wird vom Format-API aufgerufen, um zusätzlich * zu den registrierten Elementen auch alle Erweiterungselemente zu * sammeln, die für die Formatierung von Bedeutung sind.

* * @return unmodifiable list of extensions */ public List getExtensions() { return this.extensions; } /** *

Queries if this chronology has a calendar system.

* * @return {@code true} if this chronology has a calendar system else {@code false} * @see #getCalendarSystem() */ /*[deutsch] *

Ermittelt, ob diese Chronologie ein Kalendersystem hat.

* * @return {@code true} if this chronology has a calendar system else {@code false} * @see #getCalendarSystem() */ public boolean hasCalendarSystem() { return false; } /** *

Returns the associated calendar system if available.

* * @return calendar system, not {@code null} * @throws ChronoException if the calendar system is unavailable or if there is more than one variant * @see #hasCalendarSystem() */ /*[deutsch] *

Liefert das assoziierte Kalendersystem, wenn verfügbar.

* * @return calendar system, not {@code null} * @throws ChronoException if the calendar system is unavailable or if there is more than one variant * @see #hasCalendarSystem() */ public CalendarSystem getCalendarSystem() { throw new ChronoException("Calendar system is not available."); } /** *

Returns the calendar system for given calendar variant if available.

* * @param variant name of calendar variant * @return calendar system, not {@code null} * @throws ChronoException if a calendar system is unavailable for given variant (invalid variant name) * @since 3.4/4.3 * @see CalendarVariant#getVariant() */ /*[deutsch] *

Liefert das Kalendersystem zur angegebenen Kalendervariante, wenn verfügbar.

* * @param variant name of calendar variant * @return calendar system, not {@code null} * @throws ChronoException if a calendar system is unavailable for given variant (invalid variant name) * @since 3.4/4.3 * @see CalendarVariant#getVariant() */ public CalendarSystem getCalendarSystem(String variant) { throw new ChronoException("Calendar variant is not available: " + variant); } /** *

Returns a typed singleton per {@code ChronoEntity}-class.

* * @param generic type of time context * @param chronoType chronological type * @return chronology or {@code null} if not found */ /*[deutsch] *

Liefert ein getyptes Singleton pro {@code ChronoEntity}-Klasse.

* * @param generic type of time context * @param chronoType chronological type * @return chronology or {@code null} if not found */ public static > Chronology lookup(Class chronoType) { try { // Initialisierung der Klasse anstoßen, wenn noch nicht erfolgt Class.forName( chronoType.getName(), true, chronoType.getClassLoader()); } catch (ClassNotFoundException cnfe) { throw new IllegalStateException(cnfe); } Chronology ret = null; boolean purged = false; for (ChronoReference cref : CHRONOS) { Chronology chronology = cref.get(); if (chronology == null) { purged = true; } else if (chronology.getChronoType() == chronoType) { ret = chronology; break; } } if (purged) { purgeQueue(); } return cast(ret); // type-safe } /** *

Registriert die angegebene Chronologie.

* *

Die Registrierung ist zur Unterstützung der Methode {@link #lookup(Class)} gedacht und wird * einmalig nach Konstruktion einer Chronologie während des Ladens der assoziierten Entitätsklasse * aufgerufen.

* * @param chronology new instance to be registered */ static void register(Chronology chronology) { CHRONOS.add(new ChronoReference(chronology, QUEUE)); } /** *

Bestimmt eine chronologische Regel zum angegebenen Element.

* * @param Elementwerttyp * @param element chronologisches Element * @return Regelobjekt * @throws RuleNotFoundException if given element is not registered in * this chronology and there is also no element rule which can * be derived from element */ ElementRule getRule(ChronoElement element) { if (element == null) { throw new NullPointerException("Missing chronological element."); } ElementRule rule = this.ruleMap.get(element); if (rule == null) { rule = this.getDerivedRule(element, true); if (rule == null) { throw new RuleNotFoundException(this, element); } } return cast(rule); // type-safe } // optional private ElementRule getDerivedRule( ChronoElement element, boolean wantsVeto ) { if (element instanceof BasicElement) { BasicElement e = BasicElement.class.cast(element); String veto = (wantsVeto ? e.getVeto(this) : null); if (veto == null) { return e.derive(this); } else { throw new RuleNotFoundException(veto); } } return null; } // vom GC behandelte Referenzen wegräumen private static void purgeQueue() { ChronoReference cref; while ((cref = (ChronoReference) QUEUE.poll()) != null) { for (ChronoReference test : CHRONOS) { if (test.name.equals(cref.name)) { CHRONOS.remove(test); break; } } } } @SuppressWarnings("unchecked") private static T cast(Object obj) { return (T) obj; } //~ Innere Klassen ---------------------------------------------------- /** *

Builder for creating a new chronology without any time axis. * *

This class will be used during loading of a {@code ChronoEntity}-class * T in a static initializer.

* * @param generic type of time context * @author Meno Hochschild */ /*[deutsch] *

Erzeugt eine neue Chronologie ohne Zeitachse und wird * beim Laden einer {@code ChronoEntity}-Klasse T in einem * static initializer benutzt.

* * @param generic type of time context * @author Meno Hochschild */ public static class Builder> { //~ Instanzvariablen ---------------------------------------------- final Class chronoType; final boolean time4j; final ChronoMerger merger; final Map, ElementRule> ruleMap; final List extensions; //~ Konstruktoren ------------------------------------------------- /** *

Konstruiert eine neue Instanz.

* * @param chronoType chronological type * @param merger creates a new instance of T from another * source (clock or parsed values) */ Builder( Class chronoType, ChronoMerger merger ) { super(); if (merger == null) { throw new NullPointerException("Missing chronological merger."); } this.chronoType = chronoType; this.time4j = chronoType.getName().startsWith("net.time4j."); this.merger = merger; this.ruleMap = new HashMap, ElementRule>(); this.extensions = new ArrayList(); } //~ Methoden ------------------------------------------------------ /** *

Creates a builder for building a new chronological system.

* * @param generic type of time context * @param chronoType chronological type * @param chronoMerger creates a new instance of T from another * source (clock or parsed values) * @return new {@code Builder} object * @throws UnsupportedOperationException if T represents a subclass * of {@code TimePoint} */ /*[deutsch] *

Erzeugt ein Hilfsobjekt zum Bauen eines chronologischen * Systems.

* * @param generic type of time context * @param chronoType chronological type * @param chronoMerger creates a new instance of T from another * source (clock or parsed values) * @return new {@code Builder} object * @throws UnsupportedOperationException if T represents a subclass * of {@code TimePoint} */ public static > Builder setUp( Class chronoType, ChronoMerger chronoMerger ) { if (TimePoint.class.isAssignableFrom(chronoType)) { throw new UnsupportedOperationException( "This builder cannot construct a chronology " + "with a time axis, use TimeAxis.Builder instead."); } return new Builder(chronoType, chronoMerger); } /** *

Registers a new element together with its associated * element rule.

* * @param generic type of element value * @param element chronological element to be registered * @param rule rule associated with the element * @return this instance for method chaining * @throws IllegalArgumentException if given element is already * registered (duplicate) */ /*[deutsch] *

Registriert ein neues Element mitsamt der assoziierten Regel.

* * @param generic type of element value * @param element chronological element to be registered * @param rule rule associated with the element * @return this instance for method chaining * @throws IllegalArgumentException if given element is already * registered (duplicate) */ public Builder appendElement( ChronoElement element, ElementRule rule ) { this.checkElementDuplicates(element); this.ruleMap.put(element, rule); return this; } /** *

Registers a state extension which can create models with their * own state separated from standard time value context.

* * @param extension chronological extension * @return this instance for method chaining */ /*[deutsch] *

Registriert eine Zustandserweiterung, die Modelle mit einem * eigenen Zustand separat vom Zeitwertkontext erzeugen kann.

* * @param extension chronological extension * @return this instance for method chaining */ public Builder appendExtension(ChronoExtension extension) { if (extension == null) { throw new NullPointerException( "Missing chronological extension."); } else if (!this.extensions.contains(extension)) { this.extensions.add(extension); } return this; } /** *

Finishes the build of a new chronology.

* *

Internally the new chronology will be weakly registered for * {@code lookup()}. Therefore it is strongly recommended to * reference the created chronology in a static constant within * the chronological type in question.

* * @return new instance of chronology * @throws IllegalStateException if already registered * @see Chronology#lookup(Class) */ /*[deutsch] *

Schließt den Build-Vorgang ab.

* *

Intern wird die neue Chronologie für {@code lookup()} * schwach registriert. Es wird daher empfohlen, daß eine * Anwendung zusätzlich die erzeugte Chronologie in einer * eigenen statischen Konstanten referenziert.

* * @return new instance of chronology * @throws IllegalStateException if already registered * @see Chronology#lookup(Class) */ public Chronology build() { final Chronology chronology = new Chronology( this.chronoType, this.merger, this.ruleMap, this.extensions ); Chronology.register(chronology); return chronology; } private void checkElementDuplicates(ChronoElement element) { if (this.time4j) { return; } else if (element == null) { throw new NullPointerException( "Static initialization problem: " + "Check if given element statically refer " + "to any chronology causing premature class loading."); } String elementName = element.name(); for (ChronoElement key : this.ruleMap.keySet()) { if ( key.equals(element) || key.name().equals(elementName) ) { throw new IllegalArgumentException( "Element duplicate found: " + elementName); } } } } // Schwache Referenz auf ein chronologisches System private static class ChronoReference extends WeakReference> { //~ Instanzvariablen ---------------------------------------------- private final String name; //~ Konstruktoren ------------------------------------------------- ChronoReference( Chronology chronology, ReferenceQueue> queue ) { super(chronology, queue); this.name = chronology.chronoType.getName(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy