001    package toolbus.adapter;
002    
003    import java.nio.ByteBuffer;
004    
005    import jjtraveler.VisitFailure;
006    import toolbus.IOperations;
007    import toolbus.TBTermFactory;
008    import aterm.AFun;
009    import aterm.ATerm;
010    import aterm.ATermAppl;
011    import aterm.ATermBlob;
012    import aterm.ATermList;
013    import aterm.ATermPlaceholder;
014    import aterm.pure.PureFactory;
015    import aterm.pure.binary.BinaryReader;
016    import aterm.pure.binary.BinaryWriter;
017    
018    /**
019     * This class facilitates the functions a tool needs to be able to functions.
020     * 
021     * @author Arnold Lankamp
022     */
023    public abstract class AbstractTool implements IOperations{
024            protected final static PureFactory termFactory = TBTermFactory.getInstance();
025    
026            public final static String DIRECTTOOL = "direct";
027            public final static String REMOTETOOL = "remote";
028            
029            private final static int PACKBUFFERSIZE = 65536;
030            private final static AFun SYMBOL_SAF = termFactory.makeAFun("saf-encoded", 1, false);
031    
032            /**
033             * This variable MUST be set before tool interaction can take place.
034             */
035            protected ToolBridge toolBridge = null;
036    
037            /**
038             * Default constructor.
039             */
040            public AbstractTool(){
041                    super();
042            }
043    
044            /**
045             * Connects to the ToolBus.
046             * 
047             * @param args
048             *            The arguments that contain the required information for running a tool (name + id
049             *            and additionally the host + port of the ToolBus, depending on how this tool is
050             *            connected to the ToolBus).
051             * @throws Exception
052             *             Thrown when something goes wrong during the parsing of the arguments or the
053             *             establishing of the connection.
054             */
055            public abstract void connect(String[] args) throws Exception;
056            
057            /**
058             * Sets the reference ot the tool bridge we're using.
059             * 
060             * @param toolBridge
061             *            The reference ot the tool bridge we're using.
062             */
063            public void setToolBridge(ToolBridge toolBridge){
064                    this.toolBridge = toolBridge;
065            }
066    
067            /**
068             * Returns a reference to the tool bridge that we're using.
069             * 
070             * @return A reference to the tool bridge that we're using.
071             */
072            public ToolBridge getToolBridge(){
073                    return toolBridge;
074            }
075    
076            /**
077             * Returns a reference to the aterm factory.
078             * 
079             * @return A reference to the aterm factory.
080             */
081            public static PureFactory getFactory(){
082                    return termFactory;
083            }
084    
085            /**
086             * Posts an event to the ToolBus.
087             * 
088             * @param aTerm
089             *            The term that contains the data about the event.
090             */
091            public void sendEvent(ATerm aTerm){
092                    toolBridge.postEvent(aTerm);
093            }
094            
095            /**
096             * Posts a request to the ToolBus.
097             * 
098             * @param aTerm
099             *            The term that contains the data about the request.
100             * @return The response.
101             */
102            public ATermAppl sendRequest(ATerm aTerm){
103                    return toolBridge.postRequest(aTerm);
104            }
105    
106            /**
107             * Sends a disconnect request to the ToolBus.
108             * 
109             * @param aTerm
110             *            The term that contains information about the event.
111             */
112            public void disconnect(ATerm aTerm){
113                    toolBridge.send(DISCONNECT, aTerm);
114            }
115    
116            /**
117             * Terminated this Tool.
118             */
119            public void terminate(){
120                    toolBridge.terminate();
121            }
122            
123            /**
124             * Receives an acknowledgement message.
125             * 
126             * @param aTerm
127             *            The term containing information about the acknowledgement.
128             */
129            public abstract void receiveAckEvent(ATerm aTerm);
130    
131            /**
132             * Receives a termination message.
133             * 
134             * @param aTerm
135             *            The term containing information about the termination.
136             */
137            public abstract void receiveTerminate(ATerm aTerm);
138            
139            /**
140             * Packs the given term.
141             * 
142             * @param termData
143             *            The term that needs to be packed.
144             * @return
145             *            The packed version of the given term.
146             */
147            public static ATerm pack(ATerm termData){
148                    ByteBuffer packBuffer = ByteBuffer.allocate(PACKBUFFERSIZE);
149                    ATermList chunkList = termFactory.getEmpty();
150                    BinaryWriter binaryWriter = new BinaryWriter(termData);
151                    do{
152                            packBuffer.clear();
153                            try{
154                                    binaryWriter.serialize(packBuffer);
155                            }catch(VisitFailure vf){/*Intentionally ignore this useless exception.*/}
156                            
157                            int size = packBuffer.limit();
158                            byte[] data = new byte[size];
159                            packBuffer.get(data);
160                            
161                            ATermBlob chunk = termFactory.makeBlob(data);
162                            chunkList = chunkList.insert(chunk);
163                    }while(!binaryWriter.isFinished());
164                    
165                    return termFactory.makeAppl(SYMBOL_SAF, chunkList);
166            }
167            
168            /**
169             * Unpacks the given term.
170             * 
171             * @param packedTerm
172             *            The term that needs to be unpacked.
173             * @return
174             *            The unpacked version of the given term.
175             */
176            public static ATerm unpack(ATerm packedTerm){
177                    ATerm result;
178                    
179                    ATermList annos = packedTerm.getAnnotations();
180                    switch(packedTerm.getType()){
181                            case ATerm.INT:
182                            case ATerm.REAL:
183                            case ATerm.BLOB:
184                                    result = packedTerm;
185                                    break;
186                            
187                            case ATerm.PLACEHOLDER:
188                                    ATerm type = ((ATermPlaceholder) packedTerm).getPlaceholder();
189                                    ATerm unpacked_type = unpack(type);
190                                    if(unpacked_type.equals(type)){
191                                            result = packedTerm;
192                                    }else{
193                                            result = termFactory.makePlaceholder(unpacked_type);
194                                    }
195                                    break;
196                            
197                            case ATerm.APPL:
198                                    ATermAppl appl = (ATermAppl)packedTerm;
199                                    AFun fun = appl.getAFun();
200                                    if(fun == SYMBOL_SAF){
201                                            ATermList chunkList = (ATermList) appl.getArgument(0);
202                                            int nrOfChunks = chunkList.getLength();
203                                            BinaryReader binaryReader = new BinaryReader(termFactory);
204                                            do{
205                                                    ATermBlob chunk = (ATermBlob) chunkList.elementAt(--nrOfChunks);
206                                                    byte[] data = chunk.getBlobData();
207                                                    ByteBuffer unpackBuffer = ByteBuffer.wrap(data);
208                                                    
209                                                    binaryReader.deserialize(unpackBuffer);
210                                            }while(nrOfChunks > 0);
211                                            
212                                            if(!binaryReader.isDone()) throw new RuntimeException("Unpacked term was incomplete.\n");
213                                            
214                                            result = binaryReader.getRoot();
215                                    }else{
216                                            ATermList unpacked_args = termFactory.getEmpty();
217                                            
218                                            for(int i = fun.getArity() - 1; i >= 0; i--) {
219                                                    unpacked_args = unpacked_args.insert(unpack(appl.getArgument(i)));
220                                            }
221                                            
222                                            result = termFactory.makeApplList(fun, unpacked_args);
223                                    }
224                                    break;
225                            
226                            case ATerm.LIST:
227                                    ATermList list = (ATermList) packedTerm;
228                                    ATermList unpacked_list = termFactory.getEmpty();
229                                    
230                                    while(!list.isEmpty()){
231                                            unpacked_list = unpacked_list.insert(unpack(list.getFirst()));
232                                            list = list.getNext();
233                                    }
234                                    
235                                    result = unpacked_list.reverse();
236                                    break;
237                            
238                            default:
239                                    throw new RuntimeException("Unkown term type: "+packedTerm.getType());
240                    }
241                    
242                    if(annos != null){
243                            annos = (ATermList) unpack(annos);
244                            result = result.setAnnotations(annos);
245                    }
246                      
247                    return result;
248            }
249    }