package org.skyscreamer.yoga.builder;
import java.io.InputStream;
import org.skyscreamer.yoga.configuration.DefaultEntityConfigurationRegistry;
import org.skyscreamer.yoga.configuration.YogaEntityConfiguration;
import org.skyscreamer.yoga.listener.CountLimitRenderingListener;
import org.skyscreamer.yoga.listener.HrefListener;
import org.skyscreamer.yoga.listener.MetadataLinkListener;
import org.skyscreamer.yoga.listener.ModelDefinitionListener;
import org.skyscreamer.yoga.listener.NavigationLinksListener;
import org.skyscreamer.yoga.listener.RenderingListenerRegistry;
import org.skyscreamer.yoga.listener.SelectorBuilderListener;
import org.skyscreamer.yoga.listener.UriGenerator;
import org.skyscreamer.yoga.metadata.DefaultMetaDataRegistry;
import org.skyscreamer.yoga.metadata.MetaDataRegistry;
import org.skyscreamer.yoga.selector.SelectorResolver;
import org.skyscreamer.yoga.selector.parser.DynamicPropertyResolver;
import org.skyscreamer.yoga.util.ClassFinderStrategy;
import org.skyscreamer.yoga.util.DefaultClassFinderStrategy;
public class YogaBuilder
{
protected boolean _finalized = false;
protected ClassFinderStrategy _classFinderStrategy = new DefaultClassFinderStrategy();
protected RenderingListenerRegistry _registry = new RenderingListenerRegistry();
protected SelectorResolver _selectorResolver;
protected InputStream _aliasProperties = null;
protected boolean _createAllLinks = false;
private MetaDataRegistry _metaDataRegistry;
public YogaBuilder()
{
init();
}
/**
* there are cases where a user might want to override meta data registry,
* selector resolver and/or CoreSelector implementations. This would be the
* place to do it, since we have complex dependencies for those objects, and
* we need those objects set up before other setters are called.
*/
protected void init() {
_metaDataRegistry = new DefaultMetaDataRegistry();
_selectorResolver = new SelectorResolver();
_selectorResolver.getBaseSelector().setEntityConfigurationRegistry( new DefaultEntityConfigurationRegistry() );
_metaDataRegistry.setCoreSelector( this._selectorResolver.getBaseSelector() );
_metaDataRegistry.setRootMetaDataUrl( "/metadata/" );
}
// -------------- helpers ---------
protected void checkFinalized()
{
if(_finalized)
throw new IllegalStateException( "You cannot update builder information after it's been used to generate artifacts" );
}
// -------------- getters ---------
public ClassFinderStrategy getClassFinderStrategy()
{
return _classFinderStrategy;
}
public RenderingListenerRegistry getRegistry()
{
return _registry;
}
public SelectorResolver getSelectorResolver()
{
return _selectorResolver;
}
public InputStream getAliasProperties()
{
return _aliasProperties;
}
public boolean isCreateAllLinks()
{
return _createAllLinks;
}
public boolean isEnableYogaMetadata()
{
return !_metaDataRegistry.getTypes().isEmpty();
}
public MetaDataRegistry getMetaDataRegistry()
{
return _metaDataRegistry;
}
// -------------- setters ---------
/**
* Hibernate and other ORMs do some funky things to your pojo classes. A ClassFinderStrategy
* will convert the funky class object into the original version of the pojo's class.
*
* @param classFinderStrategy - a ClassFinderStrategy that knows how to introspect pojos.
*
* @see ClassFinderStrategy
*/
public void setClassFinderStrategy( ClassFinderStrategy classFinderStrategy )
{
checkFinalized();
this._classFinderStrategy = classFinderStrategy;
}
/**
* Set hand coded yoga configuration objects. This allows for either a non-annotated pojos and/or some interesting
* customization of outputs.
*
* @param entityConfigurations
*/
public void setEntityConfigurations( YogaEntityConfiguration<?>... entityConfigurations )
{
checkFinalized();
this._selectorResolver.getBaseSelector().getEntityConfigurationRegistry().register( entityConfigurations );
}
/**
* Allows you to create a RenderingListenerRegistry that contains custom event handlers
* for yoga events that relate to different sets of data being added to JSon/XML outputs.
*
* @param registry a fully formed RenderingListenerRegistry object with custom listeners
*
* @see RenderingListenerRegistry
*/
public void setRegistry( RenderingListenerRegistry registry )
{
checkFinalized();
this._registry = registry;
}
/**
* This is a mechanism of allowing small aliases to be converted into pre-configured set of aliases
* for complicated or common selectors.
*
* @param aliasProperties an Inputstream that contains key/value pairs to be injected into the Propterties
*/
public void setAliasProperties( InputStream aliasProperties )
{
checkFinalized();
this._aliasProperties = aliasProperties;
}
/**
* Set the maximum number of values that yoga should emit. This is a sanity check to make sure that
* a user can't take down your server by reading all of your data through recursive calls. A limit
* of 10,000 is reasonable.
*/
public void setOutputCountLimit( int countLimit )
{
checkFinalized();
this._registry.addListener( new CountLimitRenderingListener( countLimit ) );
}
/**
* Should yoga create metadata links as part of client requests?
*/
public void setCreateYogaLinks( boolean createAllLinks )
{
checkFinalized();
this._createAllLinks = createAllLinks;
}
/**
* Register the classes that Yoga should know about for the purposes of metadata creation.
* Yoga can do some interesting things if you register the list of pojos that are the
* return values of your REST endpoints.
*/
public void setYogaMetaDataRegisteredClasses( Class<?> ... classes )
{
checkFinalized();
this._metaDataRegistry.registerClasses( classes );
}
/**
* Set the root url to use for metadata requests.
*/
public void setRootMetaDataUrl( String rootMetaDataUrl )
{
checkFinalized();
this._metaDataRegistry.setRootMetaDataUrl( rootMetaDataUrl );
}
/**
* Should field selectors with * be considered as a shortcut for all child fields?
*/
public void setEnableStarAsAllFields( boolean starAsAll )
{
checkFinalized();
this._selectorResolver.setStarResolvesToAll( starAsAll );
}
// fluent interface
public YogaBuilder withClassFinderStrategy( ClassFinderStrategy classFinderStrategy )
{
setClassFinderStrategy( classFinderStrategy );
return this;
}
public YogaBuilder withRegistryTraverser( RenderingListenerRegistry registry )
{
setRegistry( registry );
return this;
}
public YogaBuilder withAliasProperties( InputStream propertyFile )
{
setAliasProperties( propertyFile );
return this;
}
public YogaBuilder withOutputCountLimit( int countLimit )
{
setOutputCountLimit( countLimit );
return this;
}
public YogaBuilder enableYogaLinks()
{
setCreateYogaLinks( true );
return this;
}
public YogaBuilder registerEntityConfigurations( YogaEntityConfiguration<?>... entityConfigurations )
{
setEntityConfigurations( entityConfigurations );
return this;
}
public YogaBuilder registerYogaMetaDataClasses( Class<?> ... classes )
{
setYogaMetaDataRegisteredClasses( classes );
return this;
}
public YogaBuilder enableStarAsAllFields()
{
setEnableStarAsAllFields( true );
return this;
}
// finalize!
public void finalize()
{
if( _finalized )
{
return;
}
addAliases();
if( _createAllLinks )
{
registerRenderingListeners();
}
if( _metaDataRegistry != null )
{
registerMetadataLinkListener();
}
_finalized = true;
}
protected void registerMetadataLinkListener()
{
this._registry.addListener( new MetadataLinkListener( getMetaDataRegistry() ) );
}
protected void registerRenderingListeners()
{
UriGenerator uriGenerator = new UriGenerator(
this._selectorResolver.getBaseSelector().getEntityConfigurationRegistry() );
this._registry.addListener( new HrefListener(uriGenerator) );
this._registry.addListener( new SelectorBuilderListener(uriGenerator) );
this._registry.addListener( new NavigationLinksListener(uriGenerator) );
this._registry.addListener( new ModelDefinitionListener() );
}
protected void addAliases()
{
if( this._aliasProperties != null )
{
this._selectorResolver.getSelectorParser().setAliasSelectorResolver( new DynamicPropertyResolver( _aliasProperties ) );
}
}
}