package votorola.a.position; // Copyright 2013, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. import java.io.*; import java.util.*; import votorola.a.*; import votorola.a.count.*; import votorola.g.*; import votorola.g.lang.*; /** A component pipe revision constructed nominally for the latest revision, not for a * specific {@linkplain DraftRevision draft revision path}. */ public final @ThreadSafe class ComponentPipeRevisionL extends ComponentPipeRevision { /** Partially constructs a ComponentPipeRevisionL for {@linkplain * #init(PollwikiVS,String,CountSource) init}, or {@linkplain * #init1(PollwikiVS,String,CountSource) init1} and {@linkplain * #init2(List,PollwikiVS,List,boolean) init2} to finish. * * @see #pageID() * @param _pageName will be normalized for the {@linkplain #pageName() pageName}. * @see #rev() * @see #revLatest() * @see #nominee() * @see #person() * @see #pollName() */ ComponentPipeRevisionL( final PollwikiVS wiki, int _pageID, String _pageName, int _rev, int _revLatest, String _nomineeName, String _personName, final String pollName ) throws MalformedContent { super( wiki, _pageID, _pageName, _rev, _revLatest, _nomineeName, _personName, pollName ); wildVariantName = wiki.positionPageName( nominee().username(), pollName ); } /** {@inheritDoc} Call either this method, or {@linkplain * #init1(PollwikiVS,String,CountSource) init1} and {@linkplain * #init2(List,PollwikiVS,List,boolean) init2}, not both. */ public @ThreadRestricted("constructor") void init( final PollwikiVS wiki, final String contextPersonName, final CountSource countSource ) throws IOException { init1( wiki, contextPersonName, countSource ); init2( Collections.singletonList(ComponentPipeRevisionL.this), wiki, ThrowableX.ENLIST_NONE, /*toContinue*/false ); // init2() meets API contract regarding duplicate call to init() if( candidateVariant != null ) candidateVariant.init( wiki, contextPersonName, countSource ); wildVariant.init( wiki, contextPersonName, countSource ); } /** Continues the construction of this ComponentPipeRevisionL by setting the * {@linkplain #candidateVariantName() candidate variant name} if it is not already * set and the proposed pairing appears to require it. This method may be called * multiple times. Call either {@linkplain #init(PollwikiVS,String,CountSource) * init}, or this method and {@linkplain #init2(List,PollwikiVS,List,boolean) init2}, * not both. * * @param contextPersonName the name of a person from whose viewpoint this * revision will eventually be viewed. * @param countSource for possible use during this call. */ public @ThreadRestricted("constructor") void init1( final PollwikiVS wiki, final String contextPersonName, final CountSource countSource ) throws IOException { if( candidateVariantName != null ) return; // already set if( nominee().username().equals( contextPersonName )) // from viewpoint of wild variant { final Count count = countSource.count( pollName() ); if( count != null ) try { final CountNode thisNode = count.countTablePV().get( person().email() ); if( thisNode != null ) { final String candidateName = thisNode.candidateName(); if( candidateName != null ) { // will diff the wild variant against this pipe's candidate: candidateVariantName = wiki.positionPageName( candidateName, pollName() ); } } } // Failing these, the wild variant will get a latest pairing that effectively // self-diffs. The situation will correct itself whenever the candidate // becomes known. catch( final java.sql.SQLException|javax.xml.stream.XMLStreamException x ) { throw new IOException( x ); } } } /** Finishes the construction of the specifed component pipe revisions and sets * {@linkplain #isFullyConstructed() isFullyConstructed} true for each. Call either * {@linkplain #init(PollwikiVS,String,CountSource) init}, or {@linkplain * #init1(PollwikiVS,String,CountSource) init1} and this method, not both. * * @param components the list of partially constructed component pipe revisions. * @param userXList a pre-constructed list to which any user actionable * exceptions are to be appended, or null if none was pre-constructed. Provide * {@linkplain ThrowableX#ENLIST_NONE ENLIST_NONE} to instead force the * immediate throwing of such exceptions. * @param toContinue whether to continue in the event of a missing wild variant * and to leave the incompletely constructed component pipe revision as is * (true), or to throw a NoSuchPage exception (false). * * @return the same userXList if there was one; otherwise null if no * exceptions were listed; otherwise a newly constructed list. * * @throws IllegalStateException if called a second time for a given component * pipe revision. * @throws MediaWiki.NoSuchPage if toContinue is false and a wild variant is * missing. */ public static @ThreadRestricted("constructor") List init2( final List components, final PollwikiVS wiki, List userXList, final boolean toContinue ) throws IOException { // cf. DraftPair.newDraftPair(DiffKeyParse,..) "Initialize any component pipes..." final ArrayList variantNames; { final int cN = components.size(); if( cN == 0 ) return userXList; // nothing to do variantNames = new ArrayList<>( cN ); } for( final ComponentPipeRevisionL component: components ) { variantNames.add( component.wildVariantName ); final String candidateVariantName = component.candidateVariantName; if( candidateVariantName != null ) variantNames.add( candidateVariantName ); } final LinkedList variants = new LinkedList<>(); try { userXList = CoreRevision.U.partialFromPageNames( variantNames, variants, /*componentPipeClass*/null/*even where a page name designates candidate not nominee, still it should not be a component because components are not intended to directly interconnect*/, wiki, userXList, toContinue ); } catch( final MediaWiki.NoSuchPage x ) { throw newMissingVariant( x, components, wiki ); } for( final ComponentPipeRevisionL component: components ) { // response order unpredictable, search for corresponding variant(s): for( final CoreRevision variant: variants ) { final String variantName = variant.pageName(); if( variantName.equals( component.candidateVariantName )) { component.candidateVariant = variant; } if( variantName.equals( component.wildVariantName )) component.init2( variant ); // keep searching variants, either candidate or wild may still need setting } } return userXList; } private @ThreadRestricted("constructor") void init2( CoreRevision _wildVariant ) { if( isFullyConstructed ) throw new IllegalStateException(); // per API contract of all init methods wildVariant = _wildVariant; // if( wildVariant == null ) throw new NullPointerException(); // fail fast /// done here: if( !wildVariant.pageName().equals( wildVariantName )) { throw new IllegalArgumentException( "expecting variant '" + wildVariantName + "', recieved: " + wildVariant.pageName() ); } isFullyConstructed = true; } // ------------------------------------------------------------------------------------ /** The core of the position this pipe is voting for as resolved from the candidate * variant name, or null if no candidate variant name was set during construction. */ public CoreRevision candidateVariant() { return candidateVariant; } private CoreRevision candidateVariant; // final when fully constructed /** The page name of the candidate's position core including the namespace, or null if * this information is unknown or not required. It is required only if this * component pipe revision will be viewed from the context of its own wild variant, * among other possible views. * * @see #init1(PollwikiVS,String,CountSource) */ public String candidateVariantName() { return candidateVariantName; } private String candidateVariantName; // final when fully constructed /** The wild variant indicated by this component pipe. */ public CoreRevision wildVariant() { return wildVariant; } private CoreRevision wildVariant; // final when fully constructed, never null then /** The page name of the wild variant including the namespace. */ public String wildVariantName() { return wildVariantName; } private final String wildVariantName; // - C o r e - R e v i s i o n -------------------------------------------------------- public List addDraftRevisionPath( final List path ) { path.add( rev() ); return wildVariant.addDraftRevisionPath( path ); } public CoreRevision contextView( final String contextPersonName ) { return nominee().username().equals(contextPersonName)? new ComponentPipeRevisionWV(ComponentPipeRevisionL.this): ComponentPipeRevisionL.this; } public DraftRevision draft() { return wildVariant.draft(); } /** @see #init1(PollwikiVS,String,CountSource) * @see #init2(List,PollwikiVS,List,boolean) */ public boolean isFullyConstructed() { return isFullyConstructed; } private boolean isFullyConstructed; //// P r i v a t e /////////////////////////////////////////////////////////////////////// /** Constructs an appropriate exception to throw when the position draft of a * component pipe's nominee or candidate is missing. * * @param x the original exception that signaled the missing draft. * @param components a list of component pipes including the broken one. */ private static IOException newMissingVariant( final MediaWiki.NoSuchPage x, final List components, final PollwikiVS wiki ) { final String variantName = x.pageName(); // find corresponding component: for( final ComponentPipeRevisionL co: components ) { if( variantName.equals( co.wildVariantName )) { return new MalformedContent( "nominee has no draft", x, wiki, co.rev() ); } if( variantName.equals( co.candidateVariantName )) { return new MalformedContent( "candidate has no draft", x, wiki, co.rev() ); } } assert false; return x; } }