| /*=============================================================================# |
| # Copyright (c) 2008, 2021 Stephan Wahlbrink 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, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.rj.server; |
| |
| import java.io.IOException; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import org.eclipse.statet.rj.data.RJIO; |
| import org.eclipse.statet.rj.data.RObject; |
| import org.eclipse.statet.rj.data.RObjectFactory; |
| import org.eclipse.statet.rj.data.impl.DefaultRObjectFactory; |
| |
| |
| /** |
| * Command item for main loop data exchange/evaluation |
| */ |
| public final class DataCmdItem extends MainCmdItem { |
| |
| |
| public static final class Operation { |
| |
| public static final byte NONE= 0; |
| public static final byte EXPR= 1; |
| public static final byte POINTER= 2; |
| public static final byte FCALL= 3; |
| public static final byte RDATA= 4; |
| |
| public final byte op; |
| |
| public final String name; |
| |
| public final byte source; |
| public final byte target; |
| public final boolean returnData; |
| |
| private final boolean reqSourceExpr; |
| private final boolean reqRData; |
| private final boolean reqTargetExpr; |
| |
| private Operation(final int op, final String name, |
| final byte source, final byte target, final boolean returnData) { |
| this.op= (byte)op; |
| this.name= name; |
| this.source= source; |
| this.target= target; |
| this.returnData= returnData; |
| |
| this.reqSourceExpr= (source == EXPR || source == POINTER || source == FCALL); |
| this.reqRData= (source == FCALL || source == RDATA); |
| this.reqTargetExpr= (target == EXPR || target == POINTER); |
| } |
| |
| |
| @Override |
| public String toString() { |
| return this.name; |
| } |
| |
| } |
| |
| |
| public static final byte EVAL_EXPR_VOID_OP= 0x01; |
| public static final Operation EVAL_EXPR_VOID= new Operation(EVAL_EXPR_VOID_OP, "EVAL_EXPR_VOID", //$NON-NLS-1$ |
| Operation.EXPR, Operation.NONE, false ); |
| public static final byte EVAL_FCALL_VOID_OP= 0x02; |
| public static final Operation EVAL_FCALL_VOID= new Operation(EVAL_FCALL_VOID_OP, "EVAL_FCALL_VOID", //$NON-NLS-1$ |
| Operation.FCALL, Operation.NONE, false ); |
| |
| public static final byte EVAL_EXPR_DATA_OP= 0x03; |
| public static final Operation EVAL_EXPR_DATA= new Operation(EVAL_EXPR_DATA_OP, "EVAL_EXPR_DATA", //$NON-NLS-1$ |
| Operation.EXPR, Operation.NONE, true ); |
| public static final byte EVAL_FCALL_DATA_OP= 0x04; |
| public static final Operation EVAL_FCALL_DATA= new Operation(EVAL_FCALL_DATA_OP, "EVAL_FCALL_DATA", //$NON-NLS-1$ |
| Operation.FCALL, Operation.NONE, true ); |
| public static final byte RESOLVE_DATA_OP= 0x05; |
| public static final Operation RESOLVE_DATA= new Operation(RESOLVE_DATA_OP, "RESOLVE_DATA", // EVAL_REF_DATA //$NON-NLS-1$ |
| Operation.POINTER, Operation.NONE, true ); |
| |
| public static final byte ASSIGN_DATA_OP= 0x06; |
| public static final Operation ASSIGN_DATA= new Operation(ASSIGN_DATA_OP, "ASSIGN_DATA", //$NON-NLS-1$ |
| Operation.RDATA, Operation.EXPR, false ); |
| public static final byte ASSIGN_FCALL_OP= 0x07; |
| public static final Operation ASSIGN_FCALL= new Operation(ASSIGN_FCALL_OP, "ASSIGN_FCALL", //$NON-NLS-1$ |
| Operation.FCALL, Operation.EXPR, false ); |
| |
| public static final byte FIND_DATA_OP= 0x08; |
| public static final Operation FIND_DATA= new Operation(FIND_DATA_OP, "FIND_DATA", //$NON-NLS-1$ |
| Operation.EXPR, Operation.NONE, true ); |
| |
| public static final byte EVAL_NAMESPACE_DATA_OP= 0x09; |
| public static final Operation EVAL_NAMESPACE_DATA= new Operation(EVAL_NAMESPACE_DATA_OP, "EVAL_NAMESPACE_DATA", //$NON-NLS-1$ |
| Operation.EXPR, Operation.NONE, true ); |
| public static final byte EVAL_NAMESPACE_EXPORTS_DATA_OP= 0x0A; |
| public static final Operation EVAL_NAMESPACE_EXPORTS_DATA= new Operation(EVAL_NAMESPACE_EXPORTS_DATA_OP, "EVAL_NAMESPACE_EXPORTS_DATA", //$NON-NLS-1$ |
| Operation.EXPR, Operation.NONE, true ); |
| |
| |
| private static final Operation[] OPERATIONS= new Operation[11]; |
| |
| private static final void addOp(final Operation operation) { |
| if (OPERATIONS[operation.op] != null) { |
| throw new IllegalArgumentException(); |
| } |
| OPERATIONS[operation.op]= operation; |
| } |
| |
| private static final Operation getOperation(final byte op) { |
| if (op <= 0 || op >= OPERATIONS.length) { |
| throw new UnsupportedOperationException("data op: " + op); //$NON-NLS-1$ |
| } |
| return OPERATIONS[op]; |
| } |
| |
| static { |
| addOp(EVAL_EXPR_VOID); |
| addOp(EVAL_FCALL_VOID); |
| addOp(EVAL_EXPR_DATA); |
| addOp(EVAL_FCALL_DATA); |
| addOp(RESOLVE_DATA); |
| addOp(ASSIGN_DATA); |
| addOp(ASSIGN_FCALL); |
| addOp(FIND_DATA); |
| addOp(EVAL_NAMESPACE_DATA); |
| addOp(EVAL_NAMESPACE_EXPORTS_DATA); |
| } |
| |
| |
| private static final int OV_WITHDATA= 0x02000000; |
| private static final int OV_WITHRHO= 0x04000000; |
| private static final int OV_WITHSTATUS= 0x08000000; |
| |
| |
| public static final String DEFAULT_FACTORY_ID= "default"; //$NON-NLS-1$ |
| |
| |
| static RObjectFactory gDefaultFactory; |
| |
| static final Map<String, RObjectFactory> gFactories= new ConcurrentHashMap<>(); |
| |
| private static final RObjectFactory getFactory(final String id) { |
| final RObjectFactory factory= gFactories.get(id); |
| if (factory != null) { |
| return factory; |
| } |
| return gDefaultFactory; |
| } |
| |
| static { |
| RjsComConfig.setDefaultRObjectFactory(DefaultRObjectFactory.INSTANCE); |
| } |
| |
| private static int checkCustomOptions(final int options) { |
| if ((options & (RObjectFactory.F_WITH_DBG | RObjectFactory.F_ONLY_STRUCT)) == (RObjectFactory.F_WITH_DBG | RObjectFactory.F_ONLY_STRUCT)) { |
| throw new IllegalArgumentException("options: RObjectFactory.F_LOAD_DBG & RObjectFactory.F_ONLY_STRUCT"); |
| } |
| return (options & OM_CUSTOM); |
| } |
| |
| |
| private final Operation operation; |
| |
| private byte depth; |
| private String sourceExpr; |
| private String targetExpr; |
| private RObject rdata; |
| private RObject rho; |
| |
| private String factoryId; |
| |
| private RjsStatus status; |
| |
| |
| /** |
| * Constructor for operations with returned data |
| */ |
| public DataCmdItem(final Operation op, final int options, final byte depth, |
| final String sourceExpr, final RObject data, final String targetExpr, |
| final RObject rho, |
| final String factoryId) { |
| assert (op.reqSourceExpr == (sourceExpr != null)); |
| assert (op.reqRData == (data != null)); |
| assert (op.reqTargetExpr == (targetExpr != null)); |
| assert (factoryId == null || gFactories.containsKey(factoryId)); |
| this.operation= op; |
| this.sourceExpr= sourceExpr; |
| this.targetExpr= targetExpr; |
| this.options= (OV_WAITFORCLIENT | checkCustomOptions(options)); |
| if (data != null) { |
| this.rdata= data; |
| this.options |= OV_WITHDATA; |
| } |
| if (rho != null) { |
| this.rho= rho; |
| this.options |= OV_WITHRHO; |
| } |
| this.depth= depth; |
| this.factoryId= (factoryId != null) ? factoryId : DEFAULT_FACTORY_ID; |
| } |
| |
| /** |
| * Constructor for operations without returned data: |
| */ |
| public DataCmdItem(final Operation op, final int options, |
| final String sourceExpr, final RObject data, final String targetExpr, |
| final RObject rho) { |
| assert (op.reqSourceExpr == (sourceExpr != null)); |
| assert (op.reqRData == (data != null)); |
| assert (op.reqTargetExpr == (targetExpr != null)); |
| this.operation= op; |
| this.sourceExpr= sourceExpr; |
| this.targetExpr= targetExpr; |
| this.options= (OV_WAITFORCLIENT | checkCustomOptions(options)); |
| if (data != null) { |
| this.rdata= data; |
| this.options |= OV_WITHDATA; |
| } |
| if (rho != null) { |
| this.rho= rho; |
| this.options |= OV_WITHRHO; |
| } |
| this.factoryId= ""; |
| } |
| |
| |
| /** |
| * Constructor for deserialization |
| */ |
| public DataCmdItem(final RJIO in) throws IOException { |
| this.requestId= in.readInt(); |
| this.operation= getOperation(in.readByte()); |
| this.options= in.readInt(); |
| if ((this.options & OV_WITHSTATUS) != 0) { |
| this.status= new RjsStatus(in); |
| return; |
| } |
| this.depth= in.readByte(); |
| this.factoryId= in.readString(); |
| if ((this.options & OV_ANSWER) == 0) { // request |
| if (this.operation.reqSourceExpr) { |
| this.sourceExpr= in.readString(); |
| } |
| if (this.operation.reqRData) { |
| in.flags= 0; |
| this.rdata= gDefaultFactory.readObject(in); |
| } |
| if (this.operation.reqTargetExpr) { |
| this.targetExpr= in.readString(); |
| } |
| if ((this.options & OV_WITHRHO) != 0) { |
| in.flags= 0; |
| this.rho= gDefaultFactory.readObject(in); |
| } |
| } |
| else { // answer |
| if ((this.options & OV_WITHDATA) != 0) { |
| in.flags= (this.options & 0xff); |
| this.rdata= getFactory(this.factoryId).readObject(in); |
| } |
| if ((this.options & OV_WITHRHO) != 0) { |
| in.flags= RObjectFactory.F_ONLY_STRUCT; |
| this.rho= getFactory(this.factoryId).readObject(in); |
| } |
| } |
| } |
| |
| @Override |
| public void writeExternal(final RJIO out) throws IOException { |
| out.writeInt(this.requestId); |
| out.writeByte(this.operation.op); |
| out.writeInt(this.options); |
| if ((this.options & OV_WITHSTATUS) != 0) { |
| this.status.writeExternal(out); |
| return; |
| } |
| out.writeByte(this.depth); |
| out.writeString(this.factoryId); |
| if ((this.options & OV_ANSWER) == 0) { // request |
| if (this.operation.reqSourceExpr) { |
| out.writeString(this.sourceExpr); |
| } |
| if (this.operation.reqRData) { |
| out.flags= 0; |
| gDefaultFactory.writeObject(this.rdata, out); |
| } |
| if (this.operation.reqTargetExpr) { |
| out.writeString(this.targetExpr); |
| } |
| if ((this.options & OV_WITHRHO) != 0) { |
| out.flags= 0; |
| gDefaultFactory.writeObject(this.rho, out); |
| } |
| } |
| else { // anwser |
| if ((this.options & OV_WITHDATA) != 0) { |
| out.flags= (this.options & 0xff); |
| gDefaultFactory.writeObject(this.rdata, out); |
| } |
| if ((this.options & OV_WITHRHO) != 0) { |
| out.flags= RObjectFactory.F_ONLY_STRUCT; |
| gDefaultFactory.writeObject(this.rho, out); |
| } |
| } |
| } |
| |
| |
| @Override |
| public byte getCmdType() { |
| return T_DATA_ITEM; |
| } |
| |
| |
| @Override |
| public void setAnswer(final RjsStatus status) { |
| assert (status != null); |
| if (status == RjsStatus.OK_STATUS) { |
| this.options= (this.options & OM_CLEARFORANSWER) | OV_ANSWER; |
| this.status= null; |
| this.sourceExpr= null; |
| this.rdata= null; |
| this.targetExpr= null; |
| this.rho= null; |
| } |
| else { |
| this.options= ((this.options & OM_CLEARFORANSWER) | (OV_ANSWER | OV_WITHSTATUS)); |
| this.status= status; |
| this.sourceExpr= null; |
| this.rdata= null; |
| this.targetExpr= null; |
| this.rho= null; |
| } |
| } |
| |
| public void setAnswer(final RObject rdata, final RObject rho) { |
| this.options= ((this.options & OM_CLEARFORANSWER) | OV_ANSWER); |
| if (rdata != null) { |
| this.options |= OV_WITHDATA; |
| } |
| if (rho != null) { |
| this.options |= OV_WITHRHO; |
| } |
| this.status= null; |
| this.sourceExpr= null; |
| this.rdata= rdata; |
| this.targetExpr= null; |
| this.rho= rho; |
| } |
| |
| |
| @Override |
| public byte getOp() { |
| return this.operation.op; |
| } |
| |
| public Operation getOperation() { |
| return this.operation; |
| } |
| |
| @Override |
| public boolean isOK() { |
| return (this.status == null || this.status.getSeverity() == RjsStatus.OK); |
| } |
| |
| @Override |
| public RjsStatus getStatus() { |
| return this.status; |
| } |
| |
| @Override |
| public String getDataText() { |
| return this.sourceExpr; |
| } |
| |
| public RObject getData() { |
| return this.rdata; |
| } |
| |
| public String getTargetExpr() { |
| return this.targetExpr; |
| } |
| |
| public RObject getRho() { |
| return this.rho; |
| } |
| |
| public byte getDepth() { |
| return this.depth; |
| } |
| |
| |
| @Override |
| public boolean testEquals(final MainCmdItem other) { |
| if (other instanceof DataCmdItem) { |
| final DataCmdItem otherItem= (DataCmdItem) other; |
| if (getOp() != otherItem.getOp()) { |
| return false; |
| } |
| if (this.options != otherItem.options) { |
| return false; |
| } |
| if (!((this.sourceExpr != null) ? |
| this.sourceExpr.equals(otherItem.sourceExpr) : |
| null == otherItem.sourceExpr )) { |
| return false; |
| } |
| if (!((this.rdata != null) ? |
| this.rdata.equals(otherItem.rdata) : |
| null == otherItem.rdata )) { |
| return false; |
| } |
| if (!((this.targetExpr != null) ? |
| this.targetExpr.equals(otherItem.targetExpr) : |
| null == otherItem.targetExpr )) { |
| return false; |
| } |
| if (!((this.rho != null) ? |
| this.rho.equals(otherItem.rho) : |
| null == otherItem.rho )) { |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public String toString() { |
| final StringBuffer sb= new StringBuffer(100); |
| sb.append("DataCmdItem "); |
| sb.append(this.operation.name); |
| sb.append("\n\t").append("options= 0x").append(Integer.toHexString(this.options)); |
| if (this.sourceExpr != null) { |
| sb.append("\n<SOURCE-EXPR>\n"); |
| sb.append(this.sourceExpr); |
| sb.append("\n</SOURCE-EXPR>"); |
| } |
| else { |
| sb.append("\n<SOURCE-EXPR/>"); |
| } |
| if ((this.options & OV_WITHDATA) != 0) { |
| sb.append("\n<DATA>\n"); |
| sb.append(this.rdata); |
| sb.append("\n</DATA>"); |
| } |
| else { |
| sb.append("\n<DATA/>"); |
| } |
| if (this.targetExpr != null) { |
| sb.append("\n<TARGET-EXPR>\n"); |
| sb.append(this.targetExpr); |
| sb.append("\n</TARGET-EXPR>"); |
| } |
| else { |
| sb.append("\n<TARGET-EXPR/>"); |
| } |
| if ((this.options & OV_WITHRHO) != 0) { |
| sb.append("\n<RHO>\n"); |
| sb.append(this.rho); |
| sb.append("\n</RHO>"); |
| } |
| if ((this.options & OV_WITHSTATUS) != 0) { |
| sb.append("\n<STATUS>\n"); |
| sb.append(this.status); |
| sb.append("\n</STATUS>"); |
| } |
| return sb.toString(); |
| } |
| |
| } |