NonsenseBuilder.java
- package net.zer0bandwidth.android.lib.nonsense;
- import android.content.Context;
- import net.zer0bandwidth.android.lib.R;
- import java.util.Random;
- /**
- * The canonical implementation of {@link NonsenseGenerator}.
- *
- * <p>The builder will assemble a sentence of the following form:
- * <i>adjective</i> <b>subject</b> <i>adverb</i> <b>verb</b> <i>adjective</i>
- * <b>object</b> <i>phrase</i>. The terms listed in bold (subject, verb, object)
- * will always be rendered, while the terms in italics (adjectives, adverbs,
- * additional phrases) will be added randomly based on the builder's
- * configuration, which can be tuned by creating a custom instance of the
- * {@link NonsenseBuilder.Configuration} inner class.</p>
- *
- * <p>The class uses the following string resources to get its random words:</p>
- *
- * <ul>
- * <li>{@link R.array#asNonsenseNouns}</li>
- * <li>{@link R.array#asNonsenseVerbs}</li>
- * <li>{@link R.array#asNonsenseAdjectives}</li>
- * <li>{@link R.array#asNonsenseAdverbs}</li>
- * <li>{@link R.array#asNonsensePhrases}</li>
- * </ul>
- *
- * <p>Applications using this class may choose to have overrides for any or all
- * of these string resources, in order to customize the text that might be
- * rendered in the randomized sentences.</p>
- *
- * @since zer0bandwidth-net/android 0.0.1 (#7)
- */
- @SuppressWarnings({ "unused", "WeakerAccess" }) // This is a library.
- public class NonsenseBuilder
- implements NonsenseGenerator
- {
- /**
- * This class controls aspects of a {@link NonsenseBuilder} related to the
- * probability of certain random features.
- *
- * To set non-default values, construct an instance of the class, and then
- * use its {@code set*()} methods to change any or all of the desired
- * settings. This may be done inline as follows:
- *
- * <pre>
- * NonsenseBuilder xyzzy = new NonsenseBuilder( ctx,
- * (new NonsenseBuilder.Configuration())
- * .setSubjectAdjectiveChance(25)
- * .setAdverbChance( NonsenseBuilder.Configuration.ALWAYS )
- * .setObjectAdjectiveChance(42)
- * .setObjectPhraseChance( NonsenseBuilder.Configuration.NEVER )
- * );
- * </pre>
- *
- * @since zer0bandwidth-net/android 0.0.1 (#7)
- */
- public static class Configuration
- {
- /**
- * When specifying a probability, this constant indicates that a word
- * should <i>always</i> be added to the sentence.
- */
- public static final int ALWAYS = 100 ;
- /**
- * When specifying a probability, this constant indicates that a word
- * should <i>never</i> be added to the sentence.
- */
- public static final int NEVER = 0 ;
- /**
- * The percentage chance that an adjective will be added to modify the
- * subject of the sentence.
- */
- public int m_nSubjectAdjectiveChance = 50 ;
- /**
- * The percentage chance that an adverb will be added to modify the verb
- * of the sentence.
- */
- public int m_nAdverbChance = 50 ;
- /**
- * The percentage chance that an adjective will be added to modify the
- * object of the sentence.
- */
- public int m_nObjectAdjectiveChance = 50 ;
- /**
- * The percentage chance that a prepositional phrase will be added to
- * modify the object of the sentence.
- */
- public int m_nObjectPhraseChance = 50 ;
- /**
- * Sets the probability that an adjective will be added to modify the
- * subject of the sentence.
- * If the parameter given is less than 0 or greater than 100, then the
- * new value will be silently ignored.
- * @param n an integer between 0 and 100, inclusive
- * @return (fluid)
- */
- public Configuration setSubjectAdjectiveChance( int n )
- {
- if( n < 0 || n > 100 ) return this ; // trivially
- m_nSubjectAdjectiveChance = n ;
- return this ;
- }
- /**
- * Sets the probability that an adverb will be added to modify the verb
- * of the sentence.
- * If the parameter given is less than 0 or greater than 100, then the
- * new value will be silently ignored.
- * @param n an integer between 0 and 100, inclusive
- * @return (fluid)
- */
- public Configuration setAdverbChance( int n )
- {
- if( n < 0 || n > 100 ) return this ; // trivially
- m_nAdverbChance = n ;
- return this ;
- }
- /**
- * Sets the probability that an adjective will be added to modify the
- * object of the sentence.
- * If the parameter given is less than 0 or greater than 100, then the
- * new value will be silently ignored.
- * @param n an integer between 0 and 100, inclusive
- * @return (fluid)
- */
- public Configuration setObjectAdjectiveChance( int n )
- {
- if( n < 0 || n > 100 ) return this ; // trivially
- m_nObjectAdjectiveChance = n ;
- return this ;
- }
- /**
- * Sets the probability that a prepositional phrase will be added to
- * modify the object of the sentence.
- * If the parameter given is less than 0 or greater than 100, then the
- * new value will be silently ignored.
- * @param n an integer between 0 and 100, inclusive
- * @return (fluid)
- */
- public Configuration setObjectPhraseChance( int n )
- {
- if( n < 0 || n > 100 ) return this ;
- m_nObjectPhraseChance = n ;
- return this ;
- }
- }
- /**
- * A canonical instance of a {@link NonsenseBuilder.Configuration}, using
- * the default values defined in that inner class.
- */
- protected static final Configuration CANONICAL_CONFIGURATION =
- new Configuration() ;
- /**
- * The internal RNG of the builder, used to determine the value selected for
- * each item in the sentence, and the presence of certain optional items.
- */
- protected static final Random RANDOM = new Random() ;
- /**
- * A context in which string resources are available.
- */
- protected Context m_ctx = null ;
- /**
- * The configuration settings that will control the random elements of the
- * builder. This defaults to the {@link #CANONICAL_CONFIGURATION}.
- */
- protected Configuration m_cfg = CANONICAL_CONFIGURATION ;
- /**
- * (noun) The subject of the sentence.
- * This is always set by the builder.
- */
- protected String m_sSubject = null ;
- /**
- * (adjective) A modifier of the subject.
- * This is randomly set by the builder.
- */
- protected String m_sSubjectAdjective = null ;
- /**
- * (verb) The action verb in the sentence.
- * This is always set by the builder.
- */
- protected String m_sVerb = null ;
- /**
- * (adverb) A modifier of the sentence's verb.
- * This is randomly set by the builder.
- */
- protected String m_sVerbAdverb = null ;
- /**
- * (noun) The object of the sentence.
- * This is always set by the builder.
- */
- protected String m_sObject = null ;
- /**
- * (adjective) A modifier of the object of the sentence.
- * This is randomly set by the builder.
- */
- protected String m_sObjectAdjective = null ;
- /**
- * (prepositional phrase) A phrase further modifying the object of the
- * sentence.
- * This is randomly set by the builder.
- */
- protected String m_sObjectModifier = null ;
- /**
- * A constructor which sets the resource context.
- * @param ctx a context in which string resources are available
- */
- public NonsenseBuilder( Context ctx )
- { this.setContext( ctx ) ; }
- /**
- * A constructor which sets both the resource context and a custom
- * configuration to control the random aspects of the builder.
- * @param ctx a context in which string resources are available
- * @param cfg the configuration parameters of the builder
- */
- public NonsenseBuilder( Context ctx, NonsenseBuilder.Configuration cfg )
- { this.setContext( ctx ).setConfiguration( cfg ) ; }
- /**
- * Sets the configuration parameters of the builder.
- * @param cfg specifies the randomization parameters for the builder
- * @return (fluid)
- */
- public NonsenseBuilder setConfiguration( NonsenseBuilder.Configuration cfg )
- {
- if( cfg == null ) m_cfg = CANONICAL_CONFIGURATION ;
- else m_cfg = cfg ;
- return this ;
- }
- @Override
- public NonsenseBuilder setContext( Context ctx )
- { m_ctx = ctx ; return this ; }
- /**
- * Locks a value for the subject of the sentence.
- *
- * If {@code null} is specified, then the builder's subject will be
- * "cleared", meaning that it will be chosen at random.
- *
- * @param sSubject a specific subject for the sentence, or {@code null} to
- * ensure that the subject is randomized
- * @return (fluid)
- */
- public NonsenseBuilder setSubject( String sSubject )
- { m_sSubject = sSubject ; return this ; }
- /**
- * Locks a value for the adjective modifying the subject of the sentence.
- *
- * If {@code null} is specified, then the builder might or might not add a
- * random adjective, as specified by
- * {@link Configuration#m_nSubjectAdjectiveChance}.
- *
- * @param sAdj a specific adjective to modify the subject of the sentence,
- * or {@code null} to randomize the presence and value of the
- * adjective
- * @return (fluid)
- */
- public NonsenseBuilder setSubjectAdjective( String sAdj )
- { m_sSubjectAdjective = sAdj ; return this ; }
- /**
- * Locks a value for the verb of the sentence.
- *
- * If {@code null} is specified, then the builder's verb will be "cleared",
- * meaning that it will be chosen at random.
- *
- * @param sVerb a specific verb for the sentence, or {@code null} to ensure
- * that the verb is randomized
- * @return (fluid)
- */
- public NonsenseBuilder setVerb( String sVerb )
- { m_sVerb = sVerb ; return this ; }
- /**
- * Locks a value for the adverb modifying the verb of the sentence.
- *
- * If {@code null} is specified, then the builder might or might not add a
- * random adverb, as specified by {@link Configuration#m_nAdverbChance}.
- *
- * @param sAdverb a specific adverb to modify the verb of the sentence, or
- * {@code null} to randomize the presence and value of the
- * adverb
- * @return (fluid)
- */
- public NonsenseBuilder setAdverb( String sAdverb )
- { m_sVerbAdverb = sAdverb ; return this ; }
- /**
- * Locks a value for the object of the sentence.
- *
- * If {@code null} is specified, then the builder's object will be
- * "cleared", meaning that it will be chosen at random.
- *
- * @param sObject a specific object for the sentence, or {@code null} to
- * ensure that the object is randomized
- * @return (fluid)
- */
- public NonsenseBuilder setObject( String sObject )
- { m_sObject = sObject ; return this ; }
- /**
- * Locks a value for the adjective modifying the object of the sentence.
- *
- * If {@code null} is specified, then the builder might or might not add a
- * random adjective, as specified by
- * {@link Configuration#m_nObjectAdjectiveChance}.
- *
- * @param sAdj a specific adjective to modify the object of the sentence, or
- * {@code null} to randomize the presence and value of the
- * adjective
- * @return (fluid)
- */
- public NonsenseBuilder setObjectAdjective( String sAdj )
- { m_sObjectAdjective = sAdj ; return this ; }
- /**
- * Locks a value for additional text (expected to be a prepositional phrase)
- * modifying the object of the sentence.
- *
- * If {@code null} is specified, then the builder might or might not add a
- * random phrase, as specified by
- * {@link Configuration#m_nObjectPhraseChance}.
- *
- * @param sPhrase specific text to modify the object of the sentence, or
- * {@code null} to randomize the presence and value of such
- * text
- * @return (fluid)
- */
- public NonsenseBuilder setObjectModifier( String sPhrase )
- { m_sObjectModifier = sPhrase ; return this ; }
- @Override
- public String getString()
- {
- StringBuilder sb = new StringBuilder() ;
- this.appendSubject( sb )
- .appendVerb( sb )
- .appendObject( sb )
- ;
- return sb.toString() ;
- }
- /**
- * Appends the subject and its adjective (if any) to the buffer in which the
- * sentence is being constructed.
- * @param sb the buffer in which the sentence is being constructed
- * @return (fluid)
- */
- protected NonsenseBuilder appendSubject( StringBuilder sb )
- {
- final String sSubject = ( m_sSubject == null ?
- this.getRandomNonsense( R.array.asNonsenseNouns,
- Configuration.ALWAYS ) :
- m_sSubject
- );
- final String sAdj = ( m_sSubjectAdjective == null ?
- this.getRandomNonsense( R.array.asNonsenseAdjectives,
- m_cfg.m_nSubjectAdjectiveChance ) :
- m_sSubjectAdjective
- );
- if( sAdj == null )
- { // Just capitalize and append the subject as-is and move on.
- sb.append( sSubject.substring(0,1).toUpperCase() )
- .append( sSubject.substring(1) )
- ;
- }
- else if( Utils.startsWithArticle( sSubject ) )
- { // Split the article from the subject string and insert the adjective.
- String[] asTokens = sSubject.split( " ", 2 ) ;
- switch( asTokens[0] )
- {
- case "a":
- case "an":
- sb.append( Utils.whichIndefiniteArticle( sAdj, true ) )
- .append( ' ' )
- ;
- break ;
- case "the":
- sb.append( "The " ) ;
- break ;
- default:
- sb.append( asTokens[0].substring(0,1).toUpperCase() )
- .append( asTokens[0].substring(1) )
- .append( ' ' )
- ;
- }
- sb.append( sAdj )
- .append( ' ' )
- .append( asTokens[1] )
- ;
- }
- else
- { // Capitalize the adjective and stick it before the noun.
- sb.append( sAdj.substring( 0, 1 ).toUpperCase() )
- .append( sAdj.substring(1) )
- .append( ' ' )
- .append( sSubject )
- ;
- }
- sb.append( ' ' ) ;
- return this ;
- }
- /**
- * Appends the verb and its adverb (if any) to the buffer in which the
- * sentence is being constructed.
- * @param sb the buffer in which the sentence is being constructed
- * @return (fluid)
- */
- protected NonsenseBuilder appendVerb( StringBuilder sb )
- {
- final String sVerb = ( m_sVerb == null ?
- this.getRandomNonsense( R.array.asNonsenseVerbs,
- Configuration.ALWAYS ) :
- m_sVerb
- );
- final String sAdverb = ( m_sVerbAdverb == null ?
- this.getRandomNonsense( R.array.asNonsenseAdverbs,
- m_cfg.m_nAdverbChance ) :
- m_sVerbAdverb
- );
- if( sAdverb == null )
- sb.append( sVerb ) ;
- else
- sb.append( sAdverb ).append( ' ' ).append( sVerb ) ;
- sb.append( ' ' ) ;
- return this ;
- }
- /**
- * Appends the object and its adjective and modifier phrase (if any) to the
- * buffer in which the sentence is being constructed.
- * @param sb the buffer in which the sentence is being constructed
- * @return (fluid)
- */
- protected NonsenseBuilder appendObject( StringBuilder sb )
- {
- final String sObject = ( m_sObject == null ?
- this.getRandomNonsense( R.array.asNonsenseNouns,
- Configuration.ALWAYS ) :
- m_sObject
- );
- final String sAdj = ( m_sObjectAdjective == null ?
- this.getRandomNonsense( R.array.asNonsenseAdjectives,
- m_cfg.m_nObjectAdjectiveChance ) :
- m_sObjectAdjective
- );
- final String sPhrase = ( m_sObjectModifier == null ?
- this.getRandomNonsense( R.array.asNonsensePhrases,
- m_cfg.m_nObjectPhraseChance ) :
- m_sObjectModifier
- );
- if( sAdj == null )
- sb.append( sObject ) ;
- else if( Utils.startsWithArticle( sObject ) )
- {
- String[] asTokens = sObject.split( " ", 2 ) ;
- switch( asTokens[0] )
- {
- case "a":
- case "an":
- sb.append( Utils.whichIndefiniteArticle( sAdj, false ) )
- .append( ' ' )
- ;
- break ;
- default:
- sb.append( asTokens[0] ).append( ' ' ) ;
- }
- sb.append( sAdj )
- .append( ' ' )
- .append( asTokens[1] )
- ;
- }
- else
- sb.append( sAdj ).append( ' ' ).append( sObject ) ;
- if( sPhrase != null )
- sb.append( ' ' ).append( sPhrase ) ;
- sb.append( '.' ) ;
- return this ;
- }
- /**
- * Randomly selects a string resource from the selected string array.
- * @param resStrings the resource ID of a string array
- * @param nChance chance that we should return anything at all, expressed as
- * a percentage (expected range [0,100])
- * @return a random string from that array of strings
- */
- public String getRandomNonsense( int resStrings, int nChance )
- {
- if( nChance < Configuration.ALWAYS && RANDOM.nextInt(100) >= nChance )
- return null ;
- final String[] asStrings =
- m_ctx.getResources().getStringArray( resStrings ) ;
- final int nIndex = RANDOM.nextInt( asStrings.length ) ;
- return asStrings[nIndex] ;
- }
- }