/**
*
* Copyright
* 2009-2015 Jayway Products AB
* 2016-2017 Föreningen Sambruk
*
* Licensed under AGPL, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/agpl.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package se.streamsource.streamflow.web.domain.structure.form;
import org.qi4j.api.common.Optional;
import org.qi4j.api.concern.ConcernOf;
import org.qi4j.api.concern.Concerns;
import org.qi4j.api.entity.Aggregated;
import org.qi4j.api.entity.EntityBuilder;
import org.qi4j.api.entity.Identity;
import org.qi4j.api.entity.IdentityGenerator;
import org.qi4j.api.entity.association.ManyAssociation;
import org.qi4j.api.injection.scope.Service;
import org.qi4j.api.injection.scope.Structure;
import org.qi4j.api.injection.scope.This;
import org.qi4j.api.mixin.Mixins;
import org.qi4j.api.structure.Module;
import org.qi4j.api.util.Classes;
import org.qi4j.library.constraints.annotation.GreaterThan;
import se.streamsource.streamflow.api.ErrorResources;
import se.streamsource.streamflow.api.administration.form.FieldGroupFieldValue;
import se.streamsource.streamflow.api.administration.form.FieldValue;
import se.streamsource.streamflow.infrastructure.event.domain.DomainEvent;
import se.streamsource.streamflow.util.Strings;
import se.streamsource.streamflow.web.domain.Describable;
/**
* JAVADOC
*/
@Mixins(Fields.Mixin.class)
@Concerns(Fields.MoveFieldConcern.class)
public interface Fields
{
Field createField( String name, FieldValue fieldValue );
void removeField( Field field );
void moveField( Field field, @GreaterThan(-1) Integer toIdx );
Field getFieldByName( String name );
interface Data
{
@Aggregated
ManyAssociation<Field> fields();
Field createdField( @Optional DomainEvent event, String id, FieldValue value );
void removedField( @Optional DomainEvent event, Field field );
void movedField( @Optional DomainEvent event, Field field, int toIdx );
}
abstract class Mixin
implements Fields, Data
{
@This
Data data;
@This
FieldGroupFieldsInstance fieldGroupFields;
@Service
IdentityGenerator idGen;
@Structure
Module module;
public Field createField( String name, FieldValue fieldValue )
{
Field field = createdField( null, idGen.generate( Identity.class ), fieldValue );
if ( fieldValue instanceof FieldGroupFieldValue )
{
fieldGroupFields.addFieldGroupFields( field );
}
field.changeDescription( name );
return field;
}
public void removeField( Field field )
{
if (!data.fields().contains( field ))
return;
if ( ((FieldValueDefinition.Data) field).fieldValue().get() instanceof FieldGroupFieldValue )
{
fieldGroupFields.removeFieldGroupFields( field );
}
removedField( null, field );
}
public void moveField( Field field, Integer toIdx )
{
if (!data.fields().contains( field ) || data.fields().count() <= toIdx)
return;
movedField( null, field, toIdx );
}
public Field getFieldByName( String name )
{
for (Field field : data.fields())
{
if (((Describable.Data) field).description().get().equals( name ))
return field;
}
return null;
}
public Field createdField( DomainEvent event, String id, FieldValue fieldValue )
{
EntityBuilder<Field> builder = module.unitOfWorkFactory().currentUnitOfWork().newEntityBuilder( Field.class, id );
builder.instanceFor(FieldValueDefinition.Data.class).fieldValue().set( fieldValue );
String fieldId = Classes.interfacesOf( fieldValue.getClass()).iterator().next().getSimpleName();
fieldId = fieldId.substring( 0, fieldId.length()-"FieldValue".length() );
fieldId += data.fields().count()+1;
builder.instanceFor(FieldId.Data.class).fieldId().set( fieldId );
Field field = builder.newInstance();
data.fields().add( field );
return field;
}
public void movedField( DomainEvent event, Field field, int toIdx )
{
data.fields().remove( field );
data.fields().add( toIdx, field );
}
public void removedField( DomainEvent event, Field field )
{
data.fields().remove( field );
}
}
abstract class MoveFieldConcern
extends ConcernOf<Fields>
implements Fields
{
@This Fields.Data fields;
public void moveField( Field field, @GreaterThan(-1) Integer toIdx )
{
if( ruleViolation( field, toIdx ) )
{
throw new IllegalArgumentException( ErrorResources.form_move_field_rule_violation.name() );
} else
{
next.moveField( field, toIdx );
}
}
/**
* Check if a move would result in a rule violation.
* A field with a rule may not be moved to a location before the target field of the rule!
* A field that is a target field may not switch place with a field that has the target field as rule!
* @param field The field to be moved
* @param toIdx The index to move to
* @return Whether the move will result in a rule violation or not.
*/
private boolean ruleViolation(Field field, Integer toIdx )
{
Field moveTo = fields.fields().get( toIdx.intValue() );
boolean returnValue = false;
if( (field.getRule() != null && !Strings.empty( field.getRule().field().get() ) )&&
((Identity)moveTo ).identity().get().equals( field.getRule().field().get() ) )
{
returnValue = true;
} else if( (moveTo.getRule() != null && !Strings.empty( moveTo.getRule().field().get() ) )
&& moveTo.getRule().field().get().equals( ((Identity)field).identity().get() ) )
{
returnValue = true;
}
return returnValue;
}
}
}