001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.openwire.v1;
018
019import java.io.DataInput;
020import java.io.DataOutput;
021import java.io.IOException;
022import java.lang.reflect.Constructor;
023
024import org.apache.activemq.command.DataStructure;
025import org.apache.activemq.openwire.BooleanStream;
026import org.apache.activemq.openwire.DataStreamMarshaller;
027import org.apache.activemq.openwire.OpenWireFormat;
028import org.apache.activemq.openwire.OpenWireUtil;
029import org.apache.activemq.util.ByteSequence;
030
031public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller {
032
033    public static final Constructor STACK_TRACE_ELEMENT_CONSTRUCTOR;
034    private static final int MAX_EXCEPTION_MESSAGE_SIZE = 1024;
035
036    static {
037        Constructor constructor = null;
038        try {
039            constructor = StackTraceElement.class.getConstructor(new Class[] {String.class, String.class,
040                                                                              String.class, int.class});
041        } catch (Throwable e) {
042        }
043        STACK_TRACE_ELEMENT_CONSTRUCTOR = constructor;
044    }
045
046    public abstract byte getDataStructureType();
047
048    public abstract DataStructure createObject();
049
050    public int tightMarshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException {
051        return 0;
052    }
053
054    public void tightMarshal2(OpenWireFormat wireFormat, Object o, DataOutput dataOut, BooleanStream bs)
055        throws IOException {
056    }
057
058    public void tightUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn, BooleanStream bs)
059        throws IOException {
060    }
061
062    public int tightMarshalLong1(OpenWireFormat wireFormat, long o, BooleanStream bs) throws IOException {
063        if (o == 0) {
064            bs.writeBoolean(false);
065            bs.writeBoolean(false);
066            return 0;
067        } else if ((o & 0xFFFFFFFFFFFF0000L) == 0) {
068            bs.writeBoolean(false);
069            bs.writeBoolean(true);
070            return 2;
071        } else if ((o & 0xFFFFFFFF00000000L) == 0) {
072            bs.writeBoolean(true);
073            bs.writeBoolean(false);
074            return 4;
075        } else {
076            bs.writeBoolean(true);
077            bs.writeBoolean(true);
078            return 8;
079        }
080    }
081
082    public void tightMarshalLong2(OpenWireFormat wireFormat, long o, DataOutput dataOut, BooleanStream bs)
083        throws IOException {
084        if (bs.readBoolean()) {
085            if (bs.readBoolean()) {
086                dataOut.writeLong(o);
087            } else {
088                dataOut.writeInt((int)o);
089            }
090        } else {
091            if (bs.readBoolean()) {
092                dataOut.writeShort((int)o);
093            }
094        }
095    }
096
097    public long tightUnmarshalLong(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs)
098        throws IOException {
099        if (bs.readBoolean()) {
100            if (bs.readBoolean()) {
101                return dataIn.readLong();
102            } else {
103                return toLong(dataIn.readInt());
104            }
105        } else {
106            if (bs.readBoolean()) {
107                return toLong(dataIn.readShort());
108            } else {
109                return 0;
110            }
111        }
112    }
113
114    protected long toLong(short value) {
115        // lets handle negative values
116        long answer = value;
117        return answer & 0xffffL;
118    }
119
120    protected long toLong(int value) {
121        // lets handle negative values
122        long answer = value;
123        return answer & 0xffffffffL;
124    }
125
126    protected DataStructure tightUnmarsalNestedObject(OpenWireFormat wireFormat, DataInput dataIn,
127                                                      BooleanStream bs) throws IOException {
128        return wireFormat.tightUnmarshalNestedObject(dataIn, bs);
129    }
130
131    protected int tightMarshalNestedObject1(OpenWireFormat wireFormat, DataStructure o, BooleanStream bs)
132        throws IOException {
133        return wireFormat.tightMarshalNestedObject1(o, bs);
134    }
135
136    protected void tightMarshalNestedObject2(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut,
137                                             BooleanStream bs) throws IOException {
138        wireFormat.tightMarshalNestedObject2(o, dataOut, bs);
139    }
140
141    protected DataStructure tightUnmarsalCachedObject(OpenWireFormat wireFormat, DataInput dataIn,
142                                                      BooleanStream bs) throws IOException {
143        if (wireFormat.isCacheEnabled()) {
144            if (bs.readBoolean()) {
145                short index = dataIn.readShort();
146                DataStructure object = wireFormat.tightUnmarshalNestedObject(dataIn, bs);
147                wireFormat.setInUnmarshallCache(index, object);
148                return object;
149            } else {
150                short index = dataIn.readShort();
151                return wireFormat.getFromUnmarshallCache(index);
152            }
153        } else {
154            return wireFormat.tightUnmarshalNestedObject(dataIn, bs);
155        }
156    }
157
158    protected int tightMarshalCachedObject1(OpenWireFormat wireFormat, DataStructure o, BooleanStream bs)
159        throws IOException {
160        if (wireFormat.isCacheEnabled()) {
161            Short index = wireFormat.getMarshallCacheIndex(o);
162            bs.writeBoolean(index == null);
163            if (index == null) {
164                int rc = wireFormat.tightMarshalNestedObject1(o, bs);
165                wireFormat.addToMarshallCache(o);
166                return 2 + rc;
167            } else {
168                return 2;
169            }
170        } else {
171            return wireFormat.tightMarshalNestedObject1(o, bs);
172        }
173    }
174
175    protected void tightMarshalCachedObject2(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut,
176                                             BooleanStream bs) throws IOException {
177        if (wireFormat.isCacheEnabled()) {
178            Short index = wireFormat.getMarshallCacheIndex(o);
179            if (bs.readBoolean()) {
180                dataOut.writeShort(index.shortValue());
181                wireFormat.tightMarshalNestedObject2(o, dataOut, bs);
182            } else {
183                dataOut.writeShort(index.shortValue());
184            }
185        } else {
186            wireFormat.tightMarshalNestedObject2(o, dataOut, bs);
187        }
188    }
189
190    protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs)
191        throws IOException {
192        if (bs.readBoolean()) {
193            String clazz = tightUnmarshalString(dataIn, bs);
194            String message = tightUnmarshalString(dataIn, bs);
195            Throwable o = createThrowable(clazz, message);
196            if (wireFormat.isStackTraceEnabled()) {
197                if (STACK_TRACE_ELEMENT_CONSTRUCTOR != null) {
198                    StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()];
199                    for (int i = 0; i < ss.length; i++) {
200                        try {
201                            ss[i] = (StackTraceElement)STACK_TRACE_ELEMENT_CONSTRUCTOR
202                                .newInstance(new Object[] {tightUnmarshalString(dataIn, bs),
203                                                           tightUnmarshalString(dataIn, bs),
204                                                           tightUnmarshalString(dataIn, bs),
205                                                           new Integer(dataIn.readInt())});
206                        } catch (IOException e) {
207                            throw e;
208                        } catch (Throwable e) {
209                        }
210                    }
211                    o.setStackTrace(ss);
212                } else {
213                    short size = dataIn.readShort();
214                    for (int i = 0; i < size; i++) {
215                        tightUnmarshalString(dataIn, bs);
216                        tightUnmarshalString(dataIn, bs);
217                        tightUnmarshalString(dataIn, bs);
218                        dataIn.readInt();
219                    }
220                }
221                o.initCause(tightUnmarsalThrowable(wireFormat, dataIn, bs));
222
223            }
224            return o;
225        } else {
226            return null;
227        }
228    }
229
230    private Throwable createThrowable(String className, String message) {
231        try {
232            Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader());
233            OpenWireUtil.validateIsThrowable(clazz);
234            Constructor constructor = clazz.getConstructor(new Class[] {String.class});
235            return (Throwable)constructor.newInstance(new Object[] {message});
236        } catch (IllegalArgumentException e) {
237            return e;
238        } catch (Throwable e) {
239            return new Throwable(className + ": " + message);
240        }
241    }
242
243    protected int tightMarshalThrowable1(OpenWireFormat wireFormat, Throwable o, BooleanStream bs)
244        throws IOException {
245        if (o == null) {
246            bs.writeBoolean(false);
247            return 0;
248        } else {
249            int rc = 0;
250            bs.writeBoolean(true);
251            rc += tightMarshalString1(o.getClass().getName(), bs);
252            rc += tightMarshalString1(cutMessageIfNeeded(o.getMessage()), bs);
253            if (wireFormat.isStackTraceEnabled()) {
254                rc += 2;
255                StackTraceElement[] stackTrace = o.getStackTrace();
256                for (int i = 0; i < stackTrace.length; i++) {
257                    StackTraceElement element = stackTrace[i];
258                    rc += tightMarshalString1(element.getClassName(), bs);
259                    rc += tightMarshalString1(element.getMethodName(), bs);
260                    rc += tightMarshalString1(element.getFileName(), bs);
261                    rc += 4;
262                }
263                rc += tightMarshalThrowable1(wireFormat, o.getCause(), bs);
264            }
265            return rc;
266        }
267    }
268
269    protected void tightMarshalThrowable2(OpenWireFormat wireFormat, Throwable o, DataOutput dataOut,
270                                          BooleanStream bs) throws IOException {
271        if (bs.readBoolean()) {
272            tightMarshalString2(o.getClass().getName(), dataOut, bs);
273            tightMarshalString2(cutMessageIfNeeded(o.getMessage()), dataOut, bs);
274            if (wireFormat.isStackTraceEnabled()) {
275                StackTraceElement[] stackTrace = o.getStackTrace();
276                dataOut.writeShort(stackTrace.length);
277                for (int i = 0; i < stackTrace.length; i++) {
278                    StackTraceElement element = stackTrace[i];
279                    tightMarshalString2(element.getClassName(), dataOut, bs);
280                    tightMarshalString2(element.getMethodName(), dataOut, bs);
281                    tightMarshalString2(element.getFileName(), dataOut, bs);
282                    dataOut.writeInt(element.getLineNumber());
283                }
284                tightMarshalThrowable2(wireFormat, o.getCause(), dataOut, bs);
285            }
286        }
287    }
288
289    @SuppressWarnings("deprecation")
290    protected String tightUnmarshalString(DataInput dataIn, BooleanStream bs) throws IOException {
291        if (bs.readBoolean()) {
292            if (bs.readBoolean()) {
293                int size = dataIn.readShort();
294                byte data[] = new byte[size];
295                dataIn.readFully(data);
296                // Yes deprecated, but we know what we are doing.
297                // This allows us to create a String from a ASCII byte array. (no UTF-8 decoding)
298                return new String(data, 0);
299            } else {
300                return dataIn.readUTF();
301            }
302        } else {
303            return null;
304        }
305    }
306
307    protected int tightMarshalString1(String value, BooleanStream bs) throws IOException {
308        bs.writeBoolean(value != null);
309        if (value != null) {
310
311            int strlen = value.length();
312            int utflen = 0;
313            char[] charr = new char[strlen];
314            int c = 0;
315            boolean isOnlyAscii = true;
316
317            value.getChars(0, strlen, charr, 0);
318
319            for (int i = 0; i < strlen; i++) {
320                c = charr[i];
321                if ((c >= 0x0001) && (c <= 0x007F)) {
322                    utflen++;
323                } else if (c > 0x07FF) {
324                    utflen += 3;
325                    isOnlyAscii = false;
326                } else {
327                    isOnlyAscii = false;
328                    utflen += 2;
329                }
330            }
331
332            if (utflen >= Short.MAX_VALUE) {
333                throw new IOException("Encountered a String value that is too long to encode.");
334            }
335            bs.writeBoolean(isOnlyAscii);
336            return utflen + 2;
337
338        } else {
339            return 0;
340        }
341    }
342
343    protected void tightMarshalString2(String value, DataOutput dataOut, BooleanStream bs) throws IOException {
344        if (bs.readBoolean()) {
345            // If we verified it only holds ascii values
346            if (bs.readBoolean()) {
347                dataOut.writeShort(value.length());
348                dataOut.writeBytes(value);
349            } else {
350                dataOut.writeUTF(value);
351            }
352        }
353    }
354
355    protected int tightMarshalObjectArray1(OpenWireFormat wireFormat, DataStructure[] objects,
356                                           BooleanStream bs) throws IOException {
357        if (objects != null) {
358            int rc = 0;
359            bs.writeBoolean(true);
360            rc += 2;
361            for (int i = 0; i < objects.length; i++) {
362                rc += tightMarshalNestedObject1(wireFormat, objects[i], bs);
363            }
364            return rc;
365        } else {
366            bs.writeBoolean(false);
367            return 0;
368        }
369    }
370
371    protected void tightMarshalObjectArray2(OpenWireFormat wireFormat, DataStructure[] objects,
372                                            DataOutput dataOut, BooleanStream bs) throws IOException {
373        if (bs.readBoolean()) {
374            dataOut.writeShort(objects.length);
375            for (int i = 0; i < objects.length; i++) {
376                tightMarshalNestedObject2(wireFormat, objects[i], dataOut, bs);
377            }
378        }
379    }
380
381    protected int tightMarshalConstByteArray1(byte[] data, BooleanStream bs, int i) throws IOException {
382        return i;
383    }
384
385    protected void tightMarshalConstByteArray2(byte[] data, DataOutput dataOut, BooleanStream bs, int i)
386        throws IOException {
387        dataOut.write(data, 0, i);
388    }
389
390    protected byte[] tightUnmarshalConstByteArray(DataInput dataIn, BooleanStream bs, int i)
391        throws IOException {
392        byte data[] = new byte[i];
393        dataIn.readFully(data);
394        return data;
395    }
396
397    protected int tightMarshalByteArray1(byte[] data, BooleanStream bs) throws IOException {
398        bs.writeBoolean(data != null);
399        if (data != null) {
400            return data.length + 4;
401        } else {
402            return 0;
403        }
404    }
405
406    protected void tightMarshalByteArray2(byte[] data, DataOutput dataOut, BooleanStream bs)
407        throws IOException {
408        if (bs.readBoolean()) {
409            dataOut.writeInt(data.length);
410            dataOut.write(data);
411        }
412    }
413
414    protected byte[] tightUnmarshalByteArray(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs) throws IOException {
415        byte rc[] = null;
416        if (bs.readBoolean()) {
417            int size = dataIn.readInt();
418            OpenWireUtil.validateBufferSize(wireFormat, size);
419            rc = new byte[size];
420            dataIn.readFully(rc);
421        }
422        return rc;
423    }
424
425    protected int tightMarshalByteSequence1(ByteSequence data, BooleanStream bs) throws IOException {
426        bs.writeBoolean(data != null);
427        if (data != null) {
428            return data.getLength() + 4;
429        } else {
430            return 0;
431        }
432    }
433
434    protected void tightMarshalByteSequence2(ByteSequence data, DataOutput dataOut, BooleanStream bs)
435        throws IOException {
436        if (bs.readBoolean()) {
437            dataOut.writeInt(data.getLength());
438            dataOut.write(data.getData(), data.getOffset(), data.getLength());
439        }
440    }
441
442    protected ByteSequence tightUnmarshalByteSequence(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs) throws IOException {
443        ByteSequence rc = null;
444        if (bs.readBoolean()) {
445            int size = dataIn.readInt();
446            OpenWireUtil.validateBufferSize(wireFormat, size);
447            byte[] t = new byte[size];
448            dataIn.readFully(t);
449            return new ByteSequence(t, 0, size);
450        }
451        return rc;
452    }
453
454    //
455    // The loose marshaling logic
456    //
457
458    public void looseMarshal(OpenWireFormat wireFormat, Object o, DataOutput dataOut) throws IOException {
459    }
460
461    public void looseUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn) throws IOException {
462    }
463
464    public void looseMarshalLong(OpenWireFormat wireFormat, long o, DataOutput dataOut) throws IOException {
465        dataOut.writeLong(o);
466    }
467
468    public long looseUnmarshalLong(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
469        return dataIn.readLong();
470    }
471
472    protected DataStructure looseUnmarsalNestedObject(OpenWireFormat wireFormat, DataInput dataIn)
473        throws IOException {
474        return wireFormat.looseUnmarshalNestedObject(dataIn);
475    }
476
477    protected void looseMarshalNestedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
478        throws IOException {
479        wireFormat.looseMarshalNestedObject(o, dataOut);
480    }
481
482    protected DataStructure looseUnmarsalCachedObject(OpenWireFormat wireFormat, DataInput dataIn)
483        throws IOException {
484        if (wireFormat.isCacheEnabled()) {
485            if (dataIn.readBoolean()) {
486                short index = dataIn.readShort();
487                DataStructure object = wireFormat.looseUnmarshalNestedObject(dataIn);
488                wireFormat.setInUnmarshallCache(index, object);
489                return object;
490            } else {
491                short index = dataIn.readShort();
492                return wireFormat.getFromUnmarshallCache(index);
493            }
494        } else {
495            return wireFormat.looseUnmarshalNestedObject(dataIn);
496        }
497    }
498
499    protected void looseMarshalCachedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
500        throws IOException {
501        if (wireFormat.isCacheEnabled()) {
502            Short index = wireFormat.getMarshallCacheIndex(o);
503            dataOut.writeBoolean(index == null);
504            if (index == null) {
505                index = wireFormat.addToMarshallCache(o);
506                dataOut.writeShort(index.shortValue());
507                wireFormat.looseMarshalNestedObject(o, dataOut);
508            } else {
509                dataOut.writeShort(index.shortValue());
510            }
511        } else {
512            wireFormat.looseMarshalNestedObject(o, dataOut);
513        }
514    }
515
516    protected Throwable looseUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn)
517        throws IOException {
518        if (dataIn.readBoolean()) {
519            String clazz = looseUnmarshalString(dataIn);
520            String message = looseUnmarshalString(dataIn);
521            Throwable o = createThrowable(clazz, message);
522            if (wireFormat.isStackTraceEnabled()) {
523                if (STACK_TRACE_ELEMENT_CONSTRUCTOR != null) {
524                    StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()];
525                    for (int i = 0; i < ss.length; i++) {
526                        try {
527                            ss[i] = (StackTraceElement)STACK_TRACE_ELEMENT_CONSTRUCTOR
528                                .newInstance(new Object[] {looseUnmarshalString(dataIn),
529                                                           looseUnmarshalString(dataIn),
530                                                           looseUnmarshalString(dataIn),
531                                                           new Integer(dataIn.readInt())});
532                        } catch (IOException e) {
533                            throw e;
534                        } catch (Throwable e) {
535                        }
536                    }
537                    o.setStackTrace(ss);
538                } else {
539                    short size = dataIn.readShort();
540                    for (int i = 0; i < size; i++) {
541                        looseUnmarshalString(dataIn);
542                        looseUnmarshalString(dataIn);
543                        looseUnmarshalString(dataIn);
544                        dataIn.readInt();
545                    }
546                }
547                o.initCause(looseUnmarsalThrowable(wireFormat, dataIn));
548
549            }
550            return o;
551        } else {
552            return null;
553        }
554    }
555
556    protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, DataOutput dataOut)
557        throws IOException {
558        dataOut.writeBoolean(o != null);
559        if (o != null) {
560            looseMarshalString(o.getClass().getName(), dataOut);
561            looseMarshalString(cutMessageIfNeeded(o.getMessage()), dataOut);
562            if (wireFormat.isStackTraceEnabled()) {
563                StackTraceElement[] stackTrace = o.getStackTrace();
564                dataOut.writeShort(stackTrace.length);
565                for (int i = 0; i < stackTrace.length; i++) {
566                    StackTraceElement element = stackTrace[i];
567                    looseMarshalString(element.getClassName(), dataOut);
568                    looseMarshalString(element.getMethodName(), dataOut);
569                    looseMarshalString(element.getFileName(), dataOut);
570                    dataOut.writeInt(element.getLineNumber());
571                }
572                looseMarshalThrowable(wireFormat, o.getCause(), dataOut);
573            }
574        }
575    }
576
577    protected String looseUnmarshalString(DataInput dataIn) throws IOException {
578        if (dataIn.readBoolean()) {
579            return dataIn.readUTF();
580        } else {
581            return null;
582        }
583    }
584
585    protected void looseMarshalString(String value, DataOutput dataOut) throws IOException {
586        dataOut.writeBoolean(value != null);
587        if (value != null) {
588            dataOut.writeUTF(value);
589        }
590    }
591
592    protected void looseMarshalObjectArray(OpenWireFormat wireFormat, DataStructure[] objects,
593                                           DataOutput dataOut) throws IOException {
594        dataOut.writeBoolean(objects != null);
595        if (objects != null) {
596            dataOut.writeShort(objects.length);
597            for (int i = 0; i < objects.length; i++) {
598                looseMarshalNestedObject(wireFormat, objects[i], dataOut);
599            }
600        }
601    }
602
603    protected void looseMarshalConstByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut,
604                                              int i) throws IOException {
605        dataOut.write(data, 0, i);
606    }
607
608    protected byte[] looseUnmarshalConstByteArray(DataInput dataIn, int i) throws IOException {
609        byte data[] = new byte[i];
610        dataIn.readFully(data);
611        return data;
612    }
613
614    protected void looseMarshalByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut)
615        throws IOException {
616        dataOut.writeBoolean(data != null);
617        if (data != null) {
618            dataOut.writeInt(data.length);
619            dataOut.write(data);
620        }
621    }
622
623    protected byte[] looseUnmarshalByteArray(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
624        byte rc[] = null;
625        if (dataIn.readBoolean()) {
626            int size = dataIn.readInt();
627            OpenWireUtil.validateBufferSize(wireFormat, size);
628            rc = new byte[size];
629            dataIn.readFully(rc);
630        }
631        return rc;
632    }
633
634    protected void looseMarshalByteSequence(OpenWireFormat wireFormat, ByteSequence data, DataOutput dataOut)
635        throws IOException {
636        dataOut.writeBoolean(data != null);
637        if (data != null) {
638            dataOut.writeInt(data.getLength());
639            dataOut.write(data.getData(), data.getOffset(), data.getLength());
640        }
641    }
642
643    protected ByteSequence looseUnmarshalByteSequence(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
644        ByteSequence rc = null;
645        if (dataIn.readBoolean()) {
646            int size = dataIn.readInt();
647            OpenWireUtil.validateBufferSize(wireFormat, size);
648            byte[] t = new byte[size];
649            dataIn.readFully(t);
650            rc = new ByteSequence(t, 0, size);
651        }
652        return rc;
653    }
654
655    protected String cutMessageIfNeeded(final String message) {
656        return (message.length() > MAX_EXCEPTION_MESSAGE_SIZE)?
657            message.substring(0, MAX_EXCEPTION_MESSAGE_SIZE - 3) + "..." : message;
658            
659    }
660}