blob: 68ee02aa5f3e632f4ca19de79351a58b58b51ded [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2012, 2021 Original NatTable authors and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0.
#
# SPDX-License-Identifier: EPL-2.0
#
# Contributors:
# Original NatTable authors and others - initial API and implementation (DimensionallyDependentLayer)
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ecommons.waltable.grid.layer;
import static org.eclipse.statet.ecommons.waltable.coordinate.Orientation.HORIZONTAL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.statet.ecommons.waltable.command.ILayerCommand;
import org.eclipse.statet.ecommons.waltable.coordinate.LRange;
import org.eclipse.statet.ecommons.waltable.coordinate.Orientation;
import org.eclipse.statet.ecommons.waltable.coordinate.PositionOutOfBoundsException;
import org.eclipse.statet.ecommons.waltable.layer.ForwardLayerDim;
import org.eclipse.statet.ecommons.waltable.layer.ILayer;
import org.eclipse.statet.ecommons.waltable.layer.ILayerDim;
import org.eclipse.statet.ecommons.waltable.layer.TransformLayer;
/**
* <p>A DimensionallyDependentLayer is a layer whose horizontal and vertical dimensions are dependent on the
* horizontal and vertical dimensions of other layers. A DimensionallyDependentLayer takes three constructor
* parameters: the horizontal layer that the DimensionallyDependentLayer's horizontal dimension is linked to, the
* vertical layer that the DimensionallyDependentLayer is linked to, and a base layer to which all
* non-dimensionally related ILayer method calls will be delegated to (e.g. command, event methods)
* </p>
* <p>Prime examples of dimensionally dependent layers are the column header and row header layers. For example, the
* column header layer's horizontal dimension is linked to the body layer's horizontal dimension. This means that
* whatever columns are shown in the body area will also be shown in the column header area, and vice versa. Note that
* the column header layer maintains its own vertical dimension, however, so it's vertical layer dependency would be a
* separate data layer. The same is true for the row header layer, only with the vertical instead of the horizontal
* dimension. The constructors for the column header and row header layers would therefore look something like this:
* </p>
* <pre>
* ILayer columnHeaderLayer= new DimensionallyDependentLayer(columnHeaderRowDataLayer, bodyLayer, columnHeaderRowDataLayer);
* ILayer rowHeaderLayer= new DimensionallyDependentLayer(rowHeaderColumnDataLayer, bodyLayer, rowHeaderColumnDataLayer);
* </pre>
*/
public class DimensionallyDependentLayer extends TransformLayer {
protected static class Dim extends ForwardLayerDim<DimensionallyDependentLayer> {
public Dim(final DimensionallyDependentLayer layer, final ILayerDim underlyingDim) {
super(layer, underlyingDim);
}
protected ILayerDim getBaseDim() {
return this.layer.getBaseLayer().getDim(this.orientation);
}
@Override
public long localToUnderlyingPosition(final long refPosition, final long position) {
final long id= this.underlyingDim.getPositionId(refPosition, position);
final long underlyingPosition= getBaseDim().getPositionById(id);
if (underlyingPosition == POSITION_NA) {
throw PositionOutOfBoundsException.position(position, getOrientation());
}
return underlyingPosition;
}
@Override
public long underlyingToLocalPosition(final ILayerDim sourceUnderlyingDim,
final long underlyingPosition) {
if (sourceUnderlyingDim != getBaseDim()) {
throw new IllegalArgumentException("underlyingLayer"); //$NON-NLS-1$
}
return doUnderlyingToLocalPosition(underlyingPosition);
}
@Override
public List<LRange> underlyingToLocalPositions(final ILayerDim sourceUnderlyingDim,
final Collection<LRange> underlyingPositions) {
if (sourceUnderlyingDim != getBaseDim()) {
throw new IllegalArgumentException("underlyingLayer"); //$NON-NLS-1$
}
final List<LRange> localPositions= new ArrayList<>(underlyingPositions.size());
for (final LRange underlyingPositionRange : underlyingPositions) {
if (underlyingPositionRange.start == underlyingPositionRange.end) {
final long position= doUnderlyingToLocalPosition(underlyingPositionRange.start);
localPositions.add(new LRange(position, position));
}
else {
final long first= doUnderlyingToLocalPosition(underlyingPositionRange.start);
final long last= doUnderlyingToLocalPosition(underlyingPositionRange.end - 1);
if (first <= last) {
localPositions.add(new LRange(first, last + 1));
}
}
}
return localPositions;
}
protected long doUnderlyingToLocalPosition(final long underlyingPosition) {
final long id= getBaseDim().getPositionId(underlyingPosition,
underlyingPosition );
final long position= this.underlyingDim.getPositionById(id);
if (underlyingPosition == POSITION_NA) {
throw PositionOutOfBoundsException.underlyingPosition(underlyingPosition);
}
return position;
}
@Override
public List<ILayerDim> getUnderlyingDimsByPosition(final long position) {
return Collections.<ILayerDim>singletonList(getBaseDim());
}
}
private ILayer horizontalLayerDependency;
private ILayer verticalLayerDependency;
/**
* Creates a new DimensionallyDependentLayer.
*
* @param baseLayer the underlying base layer
* @param horizontalLayerDependency the layer, the horizontal dimension is linked to
* @param verticalLayerDependency the layer, the vertical dimension is linked to
*/
public DimensionallyDependentLayer(final ILayer baseLayer,
final ILayer horizontalLayerDependency, final ILayer verticalLayerDependency) {
super(baseLayer);
setHorizontalLayerDependency(horizontalLayerDependency);
setVerticalLayerDependency(verticalLayerDependency);
}
/**
* Creates a new DimensionallyDependentLayer. The horizontal and vertical layer dependency must be set
* by calling {@link #init(ILayer, ILayer)} before the layer is used.
*
* @param baseLayer the underlying base layer
*/
protected DimensionallyDependentLayer(final ILayer baseLayer) {
super(baseLayer);
}
@Override
protected void initDims() {
if (this.horizontalLayerDependency == null || this.verticalLayerDependency == null) {
return;
}
for (final Orientation orientation : Orientation.values()) {
final ILayer dependency= getLayerDependency(orientation);
final ILayerDim dim;
if (dependency == getBaseLayer()) {
dim= new ForwardLayerDim<>(this, dependency.getDim(orientation));
}
else {
dim= new Dim(this, dependency.getDim(orientation));
}
setDim(dim);
}
}
// Dependent layer accessors
protected void setHorizontalLayerDependency(final ILayer horizontalLayerDependency) {
this.horizontalLayerDependency= horizontalLayerDependency;
// horizontalLayerDependency.addLayerListener(new ILayerListener() {
//
// public void handleLayerEvent(ILayerEvent event) {
// if (event instanceof IStructuralChangeEvent) {
// // TODO refresh horizontal structure
// }
// }
//
// });
initDims();
}
protected void setVerticalLayerDependency(final ILayer verticalLayerDependency) {
this.verticalLayerDependency= verticalLayerDependency;
// verticalLayerDependency.addLayerListener(new ILayerListener() {
//
// public void handleLayerEvent(ILayerEvent event) {
// if (event instanceof IStructuralChangeEvent) {
// // TODO refresh vertical structure
// }
// }
//
// });
initDims();
}
public ILayer getHorizontalLayerDependency() {
return this.horizontalLayerDependency;
}
public ILayer getVerticalLayerDependency() {
return this.verticalLayerDependency;
}
public ILayer getLayerDependency(final Orientation orientation) {
return (orientation == HORIZONTAL) ? this.horizontalLayerDependency : this.verticalLayerDependency;
}
public ILayer getBaseLayer() {
return getUnderlyingLayer();
}
// Commands
@Override
public boolean doCommand(final ILayerCommand command) {
if (super.doCommand(command)) {
return true;
}
// Invoke command handler(s) on the Dimensionally dependent layer ?
if (getBaseLayer() != this.horizontalLayerDependency
&& this.horizontalLayerDependency.doCommand(command)) {
return true;
}
if (getBaseLayer() != this.verticalLayerDependency
&& this.verticalLayerDependency.doCommand(command)) {
return true;
}
return false;
}
}