();
responderList.add( new CR_Doubt( Register.this ));
responderList.add( new CR_Undoubt( Register.this ));
responderList.add( new CR_Set( Register.this ));
responderList.add( new CR_Unset( Register.this ));
responderList.add( new CR_Trust( Register.this ));
responderList.add( new CR_Untrust( Register.this ));
init( responderList );
configurationContext = null; // done with it, free the memory
}
private ConfigurationContext cc() { return (ConfigurationContext)configurationContext; } // nulled after init
// ````````````````````````````````````````````````````````````````````````````````````
// Initialized early, for use in other initializers.
private final File configurationFile = cc().configurationFile;
{
Database d = new Database( cc().listDatabase() );
// try
// {
listDatabase = subserverRun.getOrInitializeDatabase( d );
// }
// finally{ listDatabase.logAndClearWarnings(); } // to show SQLState
//// but no warnings recorded there
}
// ------------------------------------------------------------------------------------
/** The directory containing this register's configuration files.
*/
@ThreadSafe File configurationDirectory() { return configurationFile.getParentFile(); }
/** The relational store of doubt signals, backing this register's doubt signaling
* network. It is a schema.table named "{@linkplain #name() register-name}.doubt",
* stored in the list database.
*
* @see #listDatabase()
*/
public @ThreadSafe DoubtSignal.Table doubtTable() { return doubtTable; } // not present in super-registers, when those are implemented
private final DoubtSignal.Table doubtTable;
{
ensureSchema( listDatabase(), name() );
doubtTable = new DoubtSignal.Table( name(), listDatabase() );
}
/** The estimated size of the electorate, if known. This a best guess of the number
* of people who would be eligible for the voter list, if they chose to register. It
* might be the population size, or it might be something less; it depends on the
* {@linkplain #listScript() configuration of the voter list}.
*
* @return estimated size of the electorate; or zero, if unknown
* @see ConfigurationContext#setElectorateSize(long)
*/
@ThreadSafe long electorateSize() { return electorateSize; }
private final long electorateSize = cc().getElectorateSize();
/** An explanation of the estimate of the size of the electorate, for the information
* of users. For example:
*
* "population of region (2008 census)"
*
*
* @see ConfigurationContext#setElectorateSizeExplanation(String)
*/
@ThreadSafe String electorateSizeExplanation() { return electorateSizeExplanation; }
private final String electorateSizeExplanation = cc().getElectorateSizeExplanation();
/** A brief description of the allowable content of this register's
* {@linkplain Registration#getLink() link field}, in sentence form.
*
* @see ConfigurationContext#setFieldDescription_link(String)
*/
public @ThreadSafe String fieldDescription_link() { return fieldDescription_link; }
private final String fieldDescription_link = cc().getFieldDescription_link();
/** An example of the allowable content for this register's
* {@linkplain Registration#getLink() link field}.
*
* @see ConfigurationContext#setFieldExample_link(String)
*/
public @ThreadSafe String fieldExample_link() { return fieldExample_link; }
private final String fieldExample_link = cc().getFieldExample_link();
/** A brief description of the allowable content of this register's
* {@linkplain Registration#getName() name field}, in sentence form.
*
* @see ConfigurationContext#setFieldDescription_name(String)
*/
public @ThreadSafe String fieldDescription_name() { return fieldDescription_name; }
private final String fieldDescription_name = cc().getFieldDescription_name();
/** An example of the allowable content for this register's
* {@linkplain Registration#getName() name field}.
*
* @see ConfigurationContext#setFieldExample_name(String)
*/
public @ThreadSafe String fieldExample_name() { return fieldExample_name; }
private final String fieldExample_name = cc().getFieldExample_name();
/** A brief description of the allowable content of this register's
* {@linkplain Registration#getNote() note field}, in sentence form.
*
* @see ConfigurationContext#setFieldDescription_note(String)
*/
public @ThreadSafe String fieldDescription_note() { return fieldDescription_note; }
private final String fieldDescription_note = cc().getFieldDescription_note();
/** An example of the allowable content for this register's
* {@linkplain Registration#getNote() note field}.
*
* @see ConfigurationContext#setFieldExample_note(String)
*/
public @ThreadSafe String fieldExample_note() { return fieldExample_note; }
private final String fieldExample_note = cc().getFieldExample_note();
/** A brief description of the allowable content of this register's
* {@linkplain Registration#getResidence() residence field}, in sentence form.
*
* @see ConfigurationContext#setFieldDescription_residence(String)
*/
public @ThreadSafe String fieldDescription_residence() { return fieldDescription_residence; }
private final String fieldDescription_residence = cc().getFieldDescription_residence();
/** An example of the allowable content for this register's
* {@linkplain Registration#getResidence() residence field}.
*
* @see ConfigurationContext#setFieldExample_residence(String)
*/
public @ThreadSafe String fieldExample_residence() { return fieldExample_residence; }
private final String fieldExample_residence = cc().getFieldExample_residence();
/** The geocoding method, for conversion of addresses to cartographic coordinates
* in the {@linkplain #trustScript() trust script}.
*
* @return geocoding method, or null if none is used
*
* @see ConfigurationContext#setGeocodingMethodGoogle(String)
*/
public @ThreadSafe Geocode.GoogleGeocoding geocodingMethod() { return geocodingMethod; }
private final Geocode.GoogleGeocoding geocodingMethod = cc().getGeocodingMethod();
/** Retrieves a voter's list node from a compiled voter list. This is a convenience
* method.
*
* @param list the voter list to use; or null, to lookup the currently
* {@linkplain #listToReport() reported list} (which involves locking overhead
* in threaded runs)
*
* @return ListNodeC or ListNodeIC
* per {@linkplain ListNodeC.Table#getOrCreate(String) getOrCreate}(voterEmail);
* or, if lookup of the list itself fails, a ListNode0 with default values
*/
public @ThreadSafe ListNode getListNode( VoterList list, final String voterEmail )
throws IOException, SQLException
{
if( list == null )
{
lock().lock();
try { list = listToReport(); }
finally { lock().unlock(); }
}
final ListNode listNode;
if( list == null ) listNode = new ListNode0( voterEmail );
else listNode = list.listNodeTable().getOrCreate( voterEmail );
return listNode;
}
/** Database for mounting the relational parts of this register's compiled voter
* lists, and its doubt table -- a reference to a shared instance (not thread safe).
*
* Although this method is thread safe, the object it returns is not;
* it has its own thread-saftety restrictions, q.v.
*
*
* @see #doubtTable()
* @see #listToReport()
* @see ConfigurationContext#listDatabase()
*/
public @ThreadSafe Database listDatabase() { return listDatabase; }
private final Database listDatabase;
/** The script 'list.js', for configuring the compilation of voter lists. It is
* located in the {@linkplain #configurationDirectory() configuration directory}.
* The language is JavaScript. There are restrictions on the {@linkplain
* votorola.g.script.JavaScriptIncluder character encoding}.
*
* @see list.js (example script)
*/
public JavaScriptIncluder listScript()
{
assert lock.isHeldByCurrentThread(); // this method is safe, but not the object
return listScript;
}
private final JavaScriptIncluder listScript = new JavaScriptIncluder(
new File( configurationDirectory(), "list.js" ));
/** The summary description for newly compiled voter lists.
*
* @see VoterList#summaryDescription()
* @see ConfigurationContext#setListSummaryDescription(String)
*/
public @ThreadSafe String listSummaryDescription() { return listSummaryDescription; }
private final String listSummaryDescription = cc().getListSummaryDescription();
/** Returns the current voter list to report, if any.
*
* @return voter list, or null if none to report
*
* @see #readyToReportLink()
*/
public VoterList listToReport() throws IOException
{
assert lock.isHeldByCurrentThread();
ReadyDirectory readyDirectory = null; // so far
if( readyToReportLink.exists() )
{
readyDirectory = new ReadyDirectory( readyToReportLink.getCanonicalPath() );
}
if( readyDirectory == null )
{
if( listToReport != null )
{
LoggerX.i(getClass()).info( readyToReportLink + ": link is lost, stopping report: " + listToReport.readyDirectory() );
listToReport = null;
}
}
else if( !readyDirectory.isMounted() )
{
LoggerX.i(getClass()).warning( readyToReportLink + ": list not mounted: " + readyDirectory );
listToReport = null;
}
else if( listToReport == null || !listToReport.readyDirectory().equals( readyDirectory ))
{
LoggerX.i(getClass()).info( readyToReportLink + ": starting new report: " + readyDirectory );
listToReport = VoterList.readObjectFromMountedListFile( readyDirectory );
listToReport.init( new ListNodeC.Table( readyDirectory, listDatabase ),
new Neighbourhood.Table( readyDirectory, listDatabase ),
new TrustEdge.Table( readyDirectory, listDatabase ));
}
return listToReport;
}
private VoterList listToReport; // lazilly set/reset through listToReport()
/** The minimum level of trust that a registrant must have, in order to pre-register
* another registrant.
*
* @see Registration#isWriteableAll()
* @see ConfigurationContext#setPreRegistrationTrustLevel(int)
*/
public @ThreadSafe int preRegistrationTrustLevel() { return preRegistrationTrustLevel; }
private final int preRegistrationTrustLevel = cc().getPreRegistrationTrustLevel();
/** The configured list of primary trust edges, for the neighbourhood trust network.
*
* @return unmodifiable list of edges
*
* @see ConfigurationContext#addPrimaryTrust(String,int)
*/
java.util.List primaryTrustList() { return primaryTrustList; }
private final ArrayListU primaryTrustList =
new ArrayListU( cc().getPrimaryTrustArray() );
/** Returns the symbolic link to the ready directory
* of the current voter list to report, if any.
*
* @return abstract path (never null) of symbolic link
*
* @see #listToReport()
*/
public @ThreadSafe File readyToReportLink() { return readyToReportLink; }
/** The script 'trust.js', for restricting the scope of trust edges among voters. It
* is located in the {@linkplain #configurationDirectory() configuration directory}.
* The language is JavaScript. There are restrictions on the {@linkplain
* votorola.g.script.JavaScriptIncluder character encoding}.
*
* @see trust/trust.js (example script)
* @see ../manual.xht#trust-script
*/
public JavaScriptIncluder trustScript()
{
assert lock.isHeldByCurrentThread(); // this method is safe, but not the object
return trustScript;
}
private final JavaScriptIncluder trustScript = new JavaScriptIncluder(
new File( configurationFile.getParentFile(), "trust.js" ));
// - E l e c t o r a l - S e r v i c e ------------------------------------------------
public @ThreadSafe @Override File configurationFile() { return configurationFile; }
public @Override Exception dispatch( final String[] argArray,
final CommandResponder.Session commandSession )
{
voterInputTable.database().logAndClearWarnings();
try{ return super.dispatch( argArray, commandSession ); }
finally{ voterInputTable.database().logAndClearWarnings(); }
}
/** @see ConfigurationContext#setSummaryDescription(String)
*/
public @Override @ThreadSafe String summaryDescription() { return summaryDescription; }
private final String summaryDescription = cc().getSummaryDescription();
/** Title of this electoral register, in title case.
*
* @see ConfigurationContext#setTitle(String)
*/
public @Override @ThreadSafe String title() { return title; }
private final String title = cc().getTitle();
// - I n p u t - S t o r e ------------------------------------------------------------
public @ThreadSafe @Override String snapshotOutputStoreFilebase()
{
return snapshotOutputStoreFilebase;
}
private final String snapshotOutputStoreFilebase =
subserverRun.subserver().cacheDirectory().getPath() + File.separator + "snapout";
private final File readyToReportLink = new File(
snapshotOutputStoreFilebase + File.separator +
name() + File.separator + "_readyList_report" );
/** @see ConfigurationContext#voterInputDatabase()
*/
public @ThreadSafe @Override VoterInputTable voterInputTable() { return voterInputTable; }
private final VoterInputTable voterInputTable;
{
Database d = new Database( cc().voterInputDatabase() );
// try
// {
d = subserverRun.getOrInitializeDatabase( d );
// }
// finally{ database.logAndClearWarnings(); } // to show SQLState
//// but no warnings recorded there
voterInputTable = new VoterInputTable( Register.this, d );
}
// ====================================================================================
/** A context for configuring a {@linkplain Register register}.
* Each register is configured by its
* {@linkplain Register#configurationFile configuration file},
* which contains a script (s) for that purpose.
* During construction of the register, an instance of this context (registerCC)
* is passed to s, via s::configureRegister(registerCC).
*/
public static @ThreadSafe final class ConfigurationContext // public class and getters, accessible by configuration scripts
{
private ConfigurationContext( ElectoralSubserver subserver, JavaScriptIncluder s )
{
configurationFile = s.scriptFile();
name = configurationFile.getParentFile().getName();
listDatabase = new ElectoralSubserver.DatabaseCC( subserver.name() );
setListSummaryDescription( "This is the current voter list, "
+ "organized by neighbourhoods. "
+ "Further information is unavailable, because the 'configureRegister' function "
+ "of script " + configurationFile + " "
+ "makes no call to 'setListSummaryDescription'." );
setSummaryDescription( "This is the electoral register. "
+ "Further information is unavailable, because the 'configureRegister' function "
+ "of script " + configurationFile + " "
+ "makes no call to 'setSummaryDescription'." );
voterInputDatabase = new ElectoralSubserver.DatabaseCC( subserver.name() );
final BundleFormatter bun = new BundleFormatter(
ResourceBundle.getBundle( "votorola.a.locale.A", Locale.getDefault() ));
setFieldDescription_link( bun.l( "a.register.Register.fieldDescription_link" ));
setFieldExample_link( bun.l( "a.register.Register.fieldExample_link" ));
setFieldDescription_name( bun.l( "a.register.Register.fieldDescription_name" ));
setFieldExample_name( bun.l( "a.register.Register.fieldExample_name" ));
setFieldDescription_note( bun.l( "a.register.Register.fieldDescription_note" ));
setFieldExample_note( bun.l( "a.register.Register.fieldExample_note" ));
setFieldDescription_residence( bun.l( "a.register.Register.fieldDescription_residence" ));
setFieldExample_residence( bun.l( "a.register.Register.fieldExample_residence" ));
}
private final File configurationFile;
private final String name;
// --------------------------------------------------------------------------------
/** @see Register#electorateSize()
* @see #setElectorateSize(long)
*/
public long getElectorateSize() { return electorateSize; }
private long electorateSize;
/** Sets the estimated size of the electorate. The default value is zero,
* meaning unknown.
*
* @see Register#electorateSize()
*/
@ThreadRestricted("constructor")
public void setElectorateSize( long electorateSize )
{
this.electorateSize = electorateSize;
}
/** @see Register#electorateSizeExplanation()
* @see #setElectorateSizeExplanation(String)
*/
public String getElectorateSizeExplanation() { return electorateSizeExplanation; }
private String electorateSizeExplanation = "number of potential registrants";
/** Sets the electorate size explanation. The default value is
* "number of potential registrants".
*
* @see Register#electorateSizeExplanation()
*/
@ThreadRestricted("constructor")
public void setElectorateSizeExplanation( String electorateSizeExplanation )
{
this.electorateSizeExplanation = electorateSizeExplanation;
}
/** @see Register#fieldDescription_link()
* @see #setFieldDescription_link(String)
*/
public String getFieldDescription_link() { return fieldDescription_link; }
private String fieldDescription_link;
/** Sets the description of the link field.
* The default value depends on the server's locale.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see Register#fieldDescription_link()
*/
@ThreadRestricted("constructor")
public void setFieldDescription_link( String fieldDescription_link )
{
if( fieldDescription_link.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.fieldDescription_link = fieldDescription_link;
}
/** @see Register#fieldExample_link()
* @see #setFieldExample_link(String)
*/
public String getFieldExample_link() { return fieldExample_link; }
private String fieldExample_link;
/** Sets the description of the link field.
* The default value depends on the server's locale.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see Register#fieldExample_link()
*/
@ThreadRestricted("constructor")
public void setFieldExample_link( String fieldExample_link )
{
if( fieldExample_link.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.fieldExample_link = fieldExample_link;
}
/** @see Register#fieldDescription_name()
* @see #setFieldDescription_name(String)
*/
public String getFieldDescription_name() { return fieldDescription_name; }
private String fieldDescription_name;
/** Sets the description of the name field.
* The default value depends on the server's locale.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see Register#fieldDescription_name()
*/
@ThreadRestricted("constructor")
public void setFieldDescription_name( String fieldDescription_name )
{
if( fieldDescription_name.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.fieldDescription_name = fieldDescription_name;
}
/** @see Register#fieldExample_name()
* @see #setFieldExample_name(String)
*/
public String getFieldExample_name() { return fieldExample_name; }
private String fieldExample_name;
/** Sets the description of the name field.
* The default value depends on the server's locale.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see Register#fieldExample_name()
*/
@ThreadRestricted("constructor")
public void setFieldExample_name( String fieldExample_name )
{
if( fieldExample_name.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.fieldExample_name = fieldExample_name;
}
/** @see Register#fieldDescription_note()
* @see #setFieldDescription_note(String)
*/
public String getFieldDescription_note() { return fieldDescription_note; }
private String fieldDescription_note;
/** Sets the description of the note field.
* The default value depends on the server's locale.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see Register#fieldDescription_note()
*/
@ThreadRestricted("constructor")
public void setFieldDescription_note( String fieldDescription_note )
{
if( fieldDescription_note.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.fieldDescription_note = fieldDescription_note;
}
/** @see Register#fieldExample_note()
* @see #setFieldExample_note(String)
*/
public String getFieldExample_note() { return fieldExample_note; }
private String fieldExample_note;
/** Sets the description of the note field.
* The default value depends on the server's locale.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see Register#fieldExample_note()
*/
@ThreadRestricted("constructor")
public void setFieldExample_note( String fieldExample_note )
{
if( fieldExample_note.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.fieldExample_note = fieldExample_note;
}
/** @see Register#fieldDescription_residence()
* @see #setFieldDescription_residence(String)
*/
public String getFieldDescription_residence() { return fieldDescription_residence; }
private String fieldDescription_residence;
/** Sets the description of the residence field.
* The default value depends on the server's locale.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see Register#fieldDescription_residence()
*/
@ThreadRestricted("constructor")
public void setFieldDescription_residence( String fieldDescription_residence )
{
if( fieldDescription_residence.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.fieldDescription_residence = fieldDescription_residence;
}
/** @see Register#fieldExample_residence()
* @see #setFieldExample_residence(String)
*/
public String getFieldExample_residence() { return fieldExample_residence; }
private String fieldExample_residence;
/** Sets the description of the residence field.
* The default value depends on the server's locale.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see Register#fieldExample_residence()
*/
@ThreadRestricted("constructor")
public void setFieldExample_residence( String fieldExample_residence )
{
if( fieldExample_residence.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.fieldExample_residence = fieldExample_residence;
}
/** @see Register#geocodingMethod()
* @see #setGeocodingMethodGoogle(String)
*/
public Geocode.GoogleGeocoding getGeocodingMethod() { return geocodingMethod; }
private Geocode.GoogleGeocoding geocodingMethod;
/** Sets the geocoding method of the register
* to {@linkplain votorola.a.Geocode.GoogleGeocoding GoogleGeocoding}.
*
* @param key per {@linkplain votorola.a.Geocode.GoogleGeocoding#key key}()
*
* @see Register#geocodingMethod()
*/
@ThreadRestricted("constructor")
public void setGeocodingMethodGoogle( String key )
{
geocodingMethod = new Geocode.GoogleGeocoding( key );
}
/** @see VoterList#summaryDescription()
* @see #setListSummaryDescription(String)
*/
public @Deprecated String getListSummaryDescription() { return listSummaryDescription; }
private String listSummaryDescription;
/** Sets the summary description for newly compiled voter lists. The default
* value is a placeholder with configuration instructions for the
* administrator.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see VoterList#summaryDescription()
*/
@Deprecated @ThreadRestricted("constructor")
public void setListSummaryDescription( String listSummaryDescription )
{
if( listSummaryDescription.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.listSummaryDescription = listSummaryDescription;
}
/** @see Register#preRegistrationTrustLevel()
* @see #setPreRegistrationTrustLevel(int)
*/
public int getPreRegistrationTrustLevel() { return preRegistrationTrustLevel; }
private int preRegistrationTrustLevel;
/** Sets the pre-registration trust level. The default value is zero.
*
* @see Register#preRegistrationTrustLevel()
*/
@ThreadRestricted("constructor")
public void setPreRegistrationTrustLevel( int preRegistrationTrustLevel )
{
this.preRegistrationTrustLevel = preRegistrationTrustLevel;
}
/** @see Register#primaryTrustList()
* @see #addPrimaryTrust(String,int)
*/
public TrustEdge.Primary[] getPrimaryTrustArray()
{
return primaryTrustList.toArray( new TrustEdge.Primary[primaryTrustList.size()] );
}
private final ArrayList primaryTrustList
= new ArrayList();
/** Adds primary trust edges to the neighbourhood network.
* By default, there are no primary trust edges (and therefore
* no trust in the network).
*
* @param voter1Email per
* TrustEdge.Primary.{@linkplain votorola.a.register.trust.TrustEdge.Primary#voter1Email voter1Email}()
* @param edgeCount per
* TrustEdge.Primary.{@linkplain votorola.a.register.trust.TrustEdge.Primary#edgeCount edgeCount}()
*
* @see Register#primaryTrustList()
*/
@ThreadRestricted("constructor")
public void addPrimaryTrust( String voter1Email, int edgeCount )
{
primaryTrustList.add( new TrustEdge.Primary( voter1Email, edgeCount ));
}
/** @see Register#summaryDescription()
* @see #setSummaryDescription(String)
*/
public String getSummaryDescription() { return summaryDescription; }
private String summaryDescription;
/** Sets the summary description of the register. The default value is a
* placeholder with configuration instructions for the administrator.
*
* @throws IllegalArgumentException if the description contains
* any newline characters, because they might render inconsistently
* across different types of user interface
* @see Register#summaryDescription()
*/
@ThreadRestricted("constructor")
public void setSummaryDescription( String summaryDescription )
{
if( summaryDescription.indexOf('\n') >= 0 ) throw new IllegalArgumentException( "argument contains a newline character" );
this.summaryDescription = summaryDescription;
}
/** @see Register#title()
* @see #setTitle(String)
*/
public String getTitle() { return title; }
private String title = "Electoral Register";
/** Sets the title of the register. The default value is "Electoral Register".
*
* @see Register#title()
*/
@ThreadRestricted("constructor")
public void setTitle( String title ) { this.title = title; }
/** The context for configuring the register's
* {@linkplain Register#listDatabase() list database}.
*/
public ElectoralSubserver.DatabaseCC listDatabase() { return listDatabase; }
private final ElectoralSubserver.DatabaseCC listDatabase;
/** The context for configuring the database of the register's
* {@linkplain Register#voterInputTable() voter input table}.
*/
public ElectoralSubserver.DatabaseCC voterInputDatabase() { return voterInputDatabase; }
private final ElectoralSubserver.DatabaseCC voterInputDatabase;
}
}