/******************************************************************************* * * Copyright 2010 Alexandru Craciun, and individual contributors as indicated * by the @authors tag. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. ******************************************************************************/ package org.netxilia.api.impl.model; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import org.netxilia.api.command.CellCommands; import org.netxilia.api.command.ICellCommand; import org.netxilia.api.command.IColumnCommand; import org.netxilia.api.command.IRowCommand; import org.netxilia.api.command.ISheetCommand; import org.netxilia.api.concurrent.IListenableFuture; import org.netxilia.api.concurrent.NetxiliaListenableFutureAdapter; import org.netxilia.api.event.ISheetEventListener; import org.netxilia.api.exception.NetxiliaBusinessException; import org.netxilia.api.formula.CyclicDependenciesException; import org.netxilia.api.formula.Formula; import org.netxilia.api.impl.concurrent.MutableFuture; import org.netxilia.api.impl.user.ISpringUserService; import org.netxilia.api.model.CellData; import org.netxilia.api.model.ColumnData; import org.netxilia.api.model.ISheet; import org.netxilia.api.model.IWorkbook; import org.netxilia.api.model.RowData; import org.netxilia.api.model.SheetData; import org.netxilia.api.model.SheetDimensions; import org.netxilia.api.model.SheetFullName; import org.netxilia.api.model.SheetType; import org.netxilia.api.model.SortSpecifier; import org.netxilia.api.reference.AreaReference; import org.netxilia.api.reference.CellReference; import org.netxilia.api.reference.Range; import org.netxilia.api.user.IAclService; import org.netxilia.api.user.Permission; import org.netxilia.api.utils.Matrix; import org.netxilia.api.value.IGenericValue; import org.springframework.util.Assert; import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; public class SheetProxy implements ISheet { private final SheetActor sheetActor; private final ExecutorService executorService; private final ISpringUserService userService; private final IAclService aclService; public SheetProxy(ExecutorService executorService, ISpringUserService userService, IAclService aclService, SheetActor actor) { Assert.notNull(executorService); Assert.notNull(userService); Assert.notNull(actor); this.sheetActor = actor; this.executorService = executorService; this.userService = userService; this.aclService = aclService; } @Override public String getName() { return sheetActor.getName(); } @Override public SheetFullName getFullName() { return sheetActor.getFullName(); } @Override public SheetType getType() { return sheetActor.getType(); } @Override public IWorkbook getWorkbook() { return sheetActor.getWorkbook(); } @Override public void addListener(ISheetEventListener listener) { sheetActor.addListener(listener); } @Override public void removeListener(ISheetEventListener listener) { sheetActor.removeListener(listener); } @Override public Executor getExecutor() { return executorService; } private <V> IListenableFuture<V> submitRead(Callable<V> callable) { aclService.checkPermission(this.getFullName(), Permission.read); return (IListenableFuture<V>) executorService.submit(new CallableWithUser<V>(userService, callable)); } private <V> IListenableFuture<V> submitWrite(Callable<V> callable) { aclService.checkPermission(this.getFullName(), Permission.write); return (IListenableFuture<V>) executorService.submit(new CallableWithUser<V>(userService, callable)); } /** * use this construction when the returned result from the actor is a Future itself. * * @param <T> * @param cmd * @return */ private <T> IListenableFuture<T> delayedWriteCall(final T cmd) { return submitWrite(new Callable<T>() { @Override public T call() throws Exception { return cmd; } }); } @Override public IListenableFuture<ICellCommand> sendFormula(final CellReference ref, final Formula formula) { return sendCommand(CellCommands.formula(new AreaReference(ref), formula)); } @Override public IListenableFuture<ICellCommand> sendValue(final CellReference ref, final IGenericValue value) { return sendCommand(CellCommands.value(new AreaReference(ref), value)); } @Override public IListenableFuture<SheetData> receiveSheet() { return submitRead(new Callable<SheetData>() { @Override public SheetData call() { return sheetActor.getSheet(); } }); } @Override public IListenableFuture<CellData> receiveCell(final CellReference ref) { return submitRead(new Callable<CellData>() { @Override public CellData call() { return sheetActor.getCell(ref); } }); } @Override public IListenableFuture<Matrix<CellData>> receiveCells(final AreaReference ref) { return submitRead(new Callable<Matrix<CellData>>() { @Override public Matrix<CellData> call() { return sheetActor.getCells(ref); } }); } @Override public IListenableFuture<ColumnData> receiveColumn(final int colIndex) { return submitRead(new Callable<ColumnData>() { @Override public ColumnData call() { return sheetActor.getColumn(colIndex); } }); } @Override public IListenableFuture<List<ColumnData>> receiveColumns(final Range range) { return submitRead(new Callable<List<ColumnData>>() { @Override public List<ColumnData> call() { return sheetActor.getColumns(range); } }); } @Override public IListenableFuture<RowData> receiveRow(final int rowIndex) { return submitRead(new Callable<RowData>() { @Override public RowData call() { return sheetActor.getRow(rowIndex); } }); } @Override public IListenableFuture<List<RowData>> receiveRows(final Range range) { return submitRead(new Callable<List<RowData>>() { @Override public List<RowData> call() { return sheetActor.getRows(range); } }); } /** * send asynchronously the command. As the result of the command is also a future, use the chain functionality to * return it to the calling client. */ @Override public IListenableFuture<ICellCommand> sendCommand(final ICellCommand command) { IListenableFuture<ICellCommand> delayedCommand = delayedWriteCall(command); // TODO - this can be created only once as no data is different. Function<ICellCommand, IListenableFuture<ICellCommand>> func = new FunctionWithUser<ICellCommand, IListenableFuture<ICellCommand>>( userService, new Function<ICellCommand, IListenableFuture<ICellCommand>>() { @Override public IListenableFuture<ICellCommand> apply(ICellCommand input) { try { return sheetActor.sendCommand(input); } catch (NetxiliaBusinessException e) { return new MutableFuture<ICellCommand>(e); } } }); return new NetxiliaListenableFutureAdapter<ICellCommand>(Futures.chain(delayedCommand, func)); } /** * send asynchronously the command. As the result of the command is also a future, use the chain functionality to * return it to the calling client. */ @Override public IListenableFuture<ICellCommand> sendCommandNoUndo(final ICellCommand command) { IListenableFuture<ICellCommand> delayedCommand = delayedWriteCall(command); Function<ICellCommand, IListenableFuture<ICellCommand>> func = new FunctionWithUser<ICellCommand, IListenableFuture<ICellCommand>>( userService, new Function<ICellCommand, IListenableFuture<ICellCommand>>() { @Override public IListenableFuture<ICellCommand> apply(ICellCommand input) { try { return sheetActor.sendCommandNoUndo(input); } catch (NetxiliaBusinessException e) { return new MutableFuture<ICellCommand>(e); } } }); return new NetxiliaListenableFutureAdapter<ICellCommand>(Futures.chain(delayedCommand, func)); } @Override public IListenableFuture<IRowCommand> sendCommand(final IRowCommand command) { return submitWrite(new Callable<IRowCommand>() { @Override public IRowCommand call() { try { return sheetActor.sendCommand(command); } catch (NetxiliaBusinessException e) { throw new RuntimeException(e); } } }); } @Override public IListenableFuture<IColumnCommand> sendCommand(final IColumnCommand command) { return submitWrite(new Callable<IColumnCommand>() { @Override public IColumnCommand call() { try { return sheetActor.sendCommand(command); } catch (NetxiliaBusinessException e) { throw new RuntimeException(e); } } }); } @Override public IListenableFuture<ISheetCommand> sendCommand(final ISheetCommand command) { return submitWrite(new Callable<ISheetCommand>() { @Override public ISheetCommand call() { try { return sheetActor.sendCommand(command); } catch (NetxiliaBusinessException e) { throw new RuntimeException(e); } } }); } @Override public IListenableFuture<SheetDimensions> getDimensions() { return submitRead(new Callable<SheetDimensions>() { @Override public SheetDimensions call() { return sheetActor.getDimensions(); } }); } /***** TO FIGURE OUT **/ @Override public IListenableFuture<Integer> sort(final SortSpecifier sortSpecifier) { return submitWrite(new Callable<Integer>() { @Override public Integer call() { try { return sheetActor.sort(sortSpecifier); } catch (CyclicDependenciesException ex) { throw new RuntimeException(ex); } catch (NetxiliaBusinessException ex) { throw new RuntimeException(ex); } } }); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + sheetActor.getFullName().hashCode(); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } SheetProxy other = (SheetProxy) obj; return sheetActor.getFullName().equals(other.getFullName()); } @Override public void setRefreshEnabled(final boolean enabled) { submitWrite(new Callable<Boolean>() { @Override public Boolean call() { try { sheetActor.setRefreshEnabled(enabled); } catch (Exception ex) { throw new RuntimeException(ex); } return true; } }); } }