/****************************************************************************** * Copyright (c) 2016 Oracle * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Konstantin Komissarchik - initial implementation and ongoing maintenance ******************************************************************************/ package org.eclipse.sapphire.modeling; import java.util.ArrayList; import java.util.List; import org.eclipse.sapphire.ElementType; import org.eclipse.sapphire.ListPropertyBinding; import org.eclipse.sapphire.Property; import org.eclipse.sapphire.PropertyBinding; import org.eclipse.sapphire.PropertyDef; import org.eclipse.sapphire.Resource; import org.eclipse.sapphire.ValueProperty; import org.eclipse.sapphire.ValuePropertyBinding; /** * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public abstract class DelimitedListBindingImpl extends ListPropertyBinding { private ListEntryResource head; @Override public ElementType type( final Resource resource ) { return property().definition().getType(); } @Override public final List<Resource> read() { final List<String> strings = split( readListString() ); final int count = strings.size(); if( count == 0 ) { this.head = null; } else { if( this.head == null ) { this.head = createListEntryResource(); } int i = 0; ListEntryResource prev = null; ListEntryResource entry = this.head; while( i < count && entry != null ) { entry.value = strings.get( i ); i++; prev = entry; entry = entry.next; } entry = prev; while( entry.next != null ) { entry.next.remove(); } while( i < count ) { entry = entry.insertAfter(); entry.value = strings.get( i ); i++; } } final List<Resource> base = new ArrayList<Resource>(); if( this.head != null ) { ListEntryResource entry = this.head; while( entry != null ) { base.add( entry ); entry = entry.next; } } return base; } @Override public final Resource insert( final ElementType type, final int position ) { ListEntryResource entry; if( this.head == null ) { entry = createListEntryResource(); this.head = entry; } else { if( position == 0 ) { entry = this.head.insertBefore(); } else { entry = this.head; for( int i = 0, n = position - 1; i < n; i++ ) { entry = entry.next; } entry = entry.insertAfter(); } } writeListString(); return entry; } @Override public void move( final Resource resource, final int position ) { final ListEntryResource x = (ListEntryResource) resource; ListEntryResource y = this.head; ListEntryResource yPrev = null; for( int i = 0; i < position; i++ ) { yPrev = y; y = y.next; } if( x != y && x.next != y ) { x.remove(); x.next = y; x.prev = yPrev; if( x.prev == null ) { this.head = x; } else { x.prev.next = x; } if( y != null ) { y.prev = x; } writeListString(); } } @Override public void remove( final Resource resource ) { ( (ListEntryResource) resource ).remove(); writeListString(); } protected char getDelimiter() { return ','; } protected abstract String readListString(); protected abstract void writeListString( String str ); private void writeListString() { final char delimiter = getDelimiter(); final String str; if( this.head == null ) { str = null; } else { final StringBuilder buf = new StringBuilder(); for( ListEntryResource entry = this.head; entry != null; entry = entry.next ) { if( entry != this.head ) { buf.append( delimiter ); } final String val = entry.value; if( val != null ) { buf.append( val ); } } str = buf.toString(); } writeListString( str ); } protected ListEntryResource createListEntryResource() { return new DefaultListEntryResource(); } private List<String> split( final String str ) { final char delimiter = getDelimiter(); final List<String> list = new ArrayList<String>(); if( str != null ) { StringBuilder buf = new StringBuilder(); for( int i = 0, n = str.length(); i < n; i++ ) { final char ch = str.charAt( i ); if( ch == delimiter ) { list.add( buf.toString() ); buf = new StringBuilder(); } else { buf.append( ch ); } } list.add( buf.toString() ); } return list; } public abstract class ListEntryResource extends Resource { private ListEntryResource prev; private ListEntryResource next; private String value; public ListEntryResource() { super( DelimitedListBindingImpl.this.property().element().resource() ); } public final String getValue() { return this.value; } public final void setValue( final String value ) { final List<String> segments = split( value ); final int count = segments.size(); if( count == 0 ) { this.value = null; } else { this.value = segments.get( 0 ); for( int i = 1; i < count; i++ ) { final ListEntryResource entry = insertAfter(); entry.setValue( segments.get( i ) ); } } writeListString(); } public final void remove() { if( this == DelimitedListBindingImpl.this.head ) { DelimitedListBindingImpl.this.head = this.next; } if( this.prev != null ) { this.prev.next = this.next; } if( this.next != null ) { this.next.prev = this.prev; } this.prev = null; this.next = null; } private ListEntryResource insertBefore() { final ListEntryResource entry = createListEntryResource(); entry.next = this; entry.prev = this.prev; this.prev = entry; if( entry.prev == null ) { DelimitedListBindingImpl.this.head = entry; } else { entry.prev.next = entry; } return entry; } private ListEntryResource insertAfter() { final ListEntryResource entry = createListEntryResource(); entry.prev = this; entry.next = this.next; this.next = entry; if( entry.next != null ) { entry.next.prev = entry; } return entry; } } private final class DefaultListEntryResource extends ListEntryResource { private ValueProperty listEntryProperty; public DefaultListEntryResource() { final ElementType listEntryType = property().definition().getType(); for( PropertyDef prop : listEntryType.properties() ) { if( this.listEntryProperty != null ) { throw new IllegalStateException(); } if( prop instanceof ValueProperty ) { this.listEntryProperty = (ValueProperty) prop; } else { throw new IllegalStateException(); } } if( this.listEntryProperty == null ) { throw new IllegalStateException(); } } @Override protected PropertyBinding createBinding( final Property property ) { if( property.definition() == this.listEntryProperty ) { return new ValuePropertyBinding() { @Override public String read() { return getValue(); } @Override public void write( final String value ) { setValue( value ); } }; } return null; } } }