Coverage Report - com.allanbank.mongodb.bson.element.JsonSerializationVisitor
 
Classes in this File Line Coverage Branch Coverage Complexity
JsonSerializationVisitor
99%
269/270
100%
40/40
3.222
 
 1  
 /*
 2  
  * #%L
 3  
  * JsonSerializationVisitor.java - mongodb-async-driver - Allanbank Consulting, Inc.
 4  
  * %%
 5  
  * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
 6  
  * %%
 7  
  * Licensed under the Apache License, Version 2.0 (the "License");
 8  
  * you may not use this file except in compliance with the License.
 9  
  * You may obtain a copy of the License at
 10  
  * 
 11  
  *      http://www.apache.org/licenses/LICENSE-2.0
 12  
  * 
 13  
  * Unless required by applicable law or agreed to in writing, software
 14  
  * distributed under the License is distributed on an "AS IS" BASIS,
 15  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  
  * See the License for the specific language governing permissions and
 17  
  * limitations under the License.
 18  
  * #L%
 19  
  */
 20  
 
 21  
 package com.allanbank.mongodb.bson.element;
 22  
 
 23  
 import java.io.IOException;
 24  
 import java.io.Writer;
 25  
 import java.text.SimpleDateFormat;
 26  
 import java.util.Date;
 27  
 import java.util.List;
 28  
 import java.util.TimeZone;
 29  
 import java.util.regex.Pattern;
 30  
 
 31  
 import com.allanbank.mongodb.bson.Document;
 32  
 import com.allanbank.mongodb.bson.Element;
 33  
 import com.allanbank.mongodb.bson.Visitor;
 34  
 import com.allanbank.mongodb.error.JsonException;
 35  
 import com.allanbank.mongodb.util.IOUtils;
 36  
 
 37  
 /**
 38  
  * JsonSerializationVisitor provides a BSON Visitor that generates a JSON
 39  
  * document.
 40  
  * 
 41  
  * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
 42  
  *         mutated in incompatible ways between any two releases of the driver.
 43  
  * @copyright 2012-2013, Allanbank Conublic sulting, Inc., All Rights Reserved
 44  
  */
 45  
 public class JsonSerializationVisitor implements Visitor {
 46  
 
 47  
     /** The platforms new line string. */
 48  1
     public static final String NL = System.getProperty("line.separator", "\n");
 49  
 
 50  
     /** A pattern to detect valid "symbol" names. */
 51  1
     public static final Pattern SYMBOL_PATTERN = Pattern
 52  
             .compile("\\p{Alpha}\\p{Alnum}*");
 53  
 
 54  
     /** The default time zone. */
 55  1
     public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
 56  
 
 57  
     /** The current indent level. */
 58  4906
     private int myIndentLevel = 0;
 59  
 
 60  
     /** If true then the visitor will write the document to 1 line. */
 61  
     private final boolean myOneLine;
 62  
 
 63  
     /** The Writer to write to. */
 64  
     private final Writer mySink;
 65  
 
 66  
     /**
 67  
      * If true then the names of the elements should be suppressed because we
 68  
      * are in an array.
 69  
      */
 70  4906
     private boolean mySuppressNames = false;
 71  
 
 72  
     /**
 73  
      * Creates a new JsonSerializationVisitor.
 74  
      * 
 75  
      * @param sink
 76  
      *            The Writer to write to.
 77  
      * @param oneLine
 78  
      *            If true then the visitor will write the document to 1 line,
 79  
      *            otherwise the visitor will write the document accross multiple
 80  
      *            lines with indenting.
 81  
      */
 82  4906
     public JsonSerializationVisitor(final Writer sink, final boolean oneLine) {
 83  4906
         mySink = sink;
 84  4906
         myOneLine = oneLine;
 85  4906
         myIndentLevel = 0;
 86  4906
     }
 87  
 
 88  
     /**
 89  
      * {@inheritDoc}
 90  
      * <p>
 91  
      * Overridden to create a JSON representation of the document's elements to
 92  
      * the writer provided when this object was created.
 93  
      * </p>
 94  
      */
 95  
     @Override
 96  
     public void visit(final List<Element> elements) {
 97  
         try {
 98  6874
             if (elements.isEmpty()) {
 99  8
                 mySink.write("{}");
 100  
             }
 101  6866
             else if ((elements.size() == 1)
 102  
                     && !(elements.get(0) instanceof DocumentElement)
 103  
                     && !(elements.get(0) instanceof ArrayElement)) {
 104  1522
                 mySink.write("{ ");
 105  
 
 106  1521
                 final boolean oldSuppress = mySuppressNames;
 107  1521
                 mySuppressNames = false;
 108  
 
 109  1521
                 elements.get(0).accept(this);
 110  
 
 111  1521
                 mySuppressNames = oldSuppress;
 112  1521
                 mySink.write(" }");
 113  1521
             }
 114  
             else {
 115  5344
                 mySink.write('{');
 116  5344
                 myIndentLevel += 1;
 117  5344
                 final boolean oldSuppress = mySuppressNames;
 118  5344
                 mySuppressNames = false;
 119  
 
 120  5344
                 boolean first = true;
 121  5344
                 for (final Element element : elements) {
 122  12517
                     if (!first) {
 123  7173
                         mySink.write(",");
 124  
                     }
 125  12517
                     nl();
 126  12517
                     element.accept(this);
 127  12517
                     first = false;
 128  12517
                 }
 129  
 
 130  5344
                 mySuppressNames = oldSuppress;
 131  5344
                 myIndentLevel -= 1;
 132  5344
                 nl();
 133  5344
                 mySink.write('}');
 134  
             }
 135  6873
             mySink.flush();
 136  
         }
 137  1
         catch (final IOException ioe) {
 138  1
             throw new JsonException(ioe);
 139  6873
         }
 140  6873
     }
 141  
 
 142  
     /**
 143  
      * {@inheritDoc}
 144  
      * <p>
 145  
      * Overridden to append a JSON representation of the array's elements to the
 146  
      * writer provided when this object was created.
 147  
      * </p>
 148  
      */
 149  
     @Override
 150  
     public void visitArray(final String name, final List<Element> elements) {
 151  
         try {
 152  2280
             writeName(name);
 153  2279
             if (elements.isEmpty()) {
 154  6
                 mySink.write("[]");
 155  
             }
 156  2273
             else if ((elements.size() == 1)
 157  
                     && !(elements.get(0) instanceof DocumentElement)
 158  
                     && !(elements.get(0) instanceof ArrayElement)) {
 159  6
                 mySink.write("[ ");
 160  6
                 final boolean oldSuppress = mySuppressNames;
 161  6
                 mySuppressNames = true;
 162  
 
 163  6
                 elements.get(0).accept(this);
 164  
 
 165  6
                 mySuppressNames = oldSuppress;
 166  6
                 mySink.write(" ]");
 167  6
             }
 168  
             else {
 169  2267
                 mySink.write("[");
 170  2267
                 myIndentLevel += 1;
 171  2267
                 final boolean oldSuppress = mySuppressNames;
 172  2267
                 mySuppressNames = true;
 173  
 
 174  2267
                 boolean first = true;
 175  2267
                 for (final Element element : elements) {
 176  2289
                     if (!first) {
 177  22
                         mySink.write(", ");
 178  
                     }
 179  2289
                     nl();
 180  2289
                     element.accept(this);
 181  2289
                     first = false;
 182  2289
                 }
 183  
 
 184  2267
                 mySuppressNames = oldSuppress;
 185  2267
                 myIndentLevel -= 1;
 186  2267
                 nl();
 187  2267
                 mySink.append(']');
 188  
             }
 189  2279
             mySink.flush();
 190  
         }
 191  1
         catch (final IOException ioe) {
 192  1
             throw new JsonException(ioe);
 193  2279
         }
 194  2279
     }
 195  
 
 196  
     /**
 197  
      * {@inheritDoc}
 198  
      * <p>
 199  
      * Overridden to append a JSON representation of the binary element to the
 200  
      * writer provided when this object was created. This method generates the
 201  
      * MongoDB standard BinData(...) JSON extension.
 202  
      * </p>
 203  
      */
 204  
     @Override
 205  
     public void visitBinary(final String name, final byte subType,
 206  
             final byte[] data) {
 207  
         try {
 208  12
             writeName(name);
 209  11
             mySink.write("BinData( ");
 210  11
             mySink.write(Integer.toString(subType));
 211  11
             mySink.write(", '");
 212  11
             mySink.write(IOUtils.toBase64(data));
 213  11
             mySink.write("' )");
 214  11
             mySink.flush();
 215  
         }
 216  1
         catch (final IOException ioe) {
 217  1
             throw new JsonException(ioe);
 218  11
         }
 219  11
     }
 220  
 
 221  
     /**
 222  
      * {@inheritDoc}
 223  
      * <p>
 224  
      * Overridden to append a JSON representation of the boolean element to the
 225  
      * writer provided when this object was created.
 226  
      * </p>
 227  
      */
 228  
     @Override
 229  
     public void visitBoolean(final String name, final boolean value) {
 230  
         try {
 231  28
             writeName(name);
 232  27
             mySink.write(Boolean.toString(value));
 233  27
             mySink.flush();
 234  
         }
 235  1
         catch (final IOException ioe) {
 236  1
             throw new JsonException(ioe);
 237  27
         }
 238  27
     }
 239  
 
 240  
     /**
 241  
      * {@inheritDoc}
 242  
      * <p>
 243  
      * Overridden to append a JSON representation of the DBPointer element to
 244  
      * the writer provided when this object was created. This method generates
 245  
      * the non-standard DBPointer(...) JSON extension.
 246  
      * </p>
 247  
      */
 248  
     @Override
 249  
     public void visitDBPointer(final String name, final String databaseName,
 250  
             final String collectionName, final ObjectId id) {
 251  
         try {
 252  3
             writeName(name);
 253  2
             mySink.write("DBPointer( ");
 254  2
             writeQuotedString(databaseName);
 255  2
             mySink.write(", ");
 256  2
             writeQuotedString(collectionName);
 257  2
             mySink.write(", ");
 258  2
             writeObjectId(id);
 259  2
             mySink.write(" )");
 260  2
             mySink.flush();
 261  
         }
 262  1
         catch (final IOException ioe) {
 263  1
             throw new JsonException(ioe);
 264  2
         }
 265  2
     }
 266  
 
 267  
     /**
 268  
      * {@inheritDoc}
 269  
      * <p>
 270  
      * Overridden to append a JSON representation of the sub-document element to
 271  
      * the writer provided when this object was created.
 272  
      * </p>
 273  
      */
 274  
     @Override
 275  
     public void visitDocument(final String name, final List<Element> elements) {
 276  
         try {
 277  4522
             writeName(name);
 278  4521
             visit(elements);
 279  
         }
 280  1
         catch (final IOException ioe) {
 281  1
             throw new JsonException(ioe);
 282  4521
         }
 283  4521
     }
 284  
 
 285  
     /**
 286  
      * {@inheritDoc}
 287  
      * <p>
 288  
      * Overridden to append a JSON representation of the double element to the
 289  
      * writer provided when this object was created.
 290  
      * </p>
 291  
      */
 292  
     @Override
 293  
     public void visitDouble(final String name, final double value) {
 294  
         try {
 295  20
             writeName(name);
 296  19
             mySink.write(Double.toString(value));
 297  19
             mySink.flush();
 298  
         }
 299  1
         catch (final IOException ioe) {
 300  1
             throw new JsonException(ioe);
 301  19
         }
 302  19
     }
 303  
 
 304  
     /**
 305  
      * {@inheritDoc}
 306  
      * <p>
 307  
      * Overridden to append a JSON representation of the integer element to the
 308  
      * writer provided when this object was created.
 309  
      * </p>
 310  
      */
 311  
     @Override
 312  
     public void visitInteger(final String name, final int value) {
 313  
         try {
 314  4901
             writeName(name);
 315  4900
             mySink.write(Integer.toString(value));
 316  4900
             mySink.flush();
 317  
         }
 318  1
         catch (final IOException ioe) {
 319  1
             throw new JsonException(ioe);
 320  4900
         }
 321  4900
     }
 322  
 
 323  
     /**
 324  
      * {@inheritDoc}
 325  
      * <p>
 326  
      * Overridden to append a JSON representation of the JavaScript element to
 327  
      * the writer provided when this object was created. This method writes the
 328  
      * elements as a <code>{ $code : &lt;code&gt; }</code> sub-document.
 329  
      * </p>
 330  
      */
 331  
     @Override
 332  
     public void visitJavaScript(final String name, final String code) {
 333  
         try {
 334  3
             writeName(name);
 335  2
             mySink.write("{ $code : ");
 336  2
             writeQuotedString(code);
 337  2
             mySink.write(" }");
 338  2
             mySink.flush();
 339  
         }
 340  1
         catch (final IOException ioe) {
 341  1
             throw new JsonException(ioe);
 342  2
         }
 343  2
     }
 344  
 
 345  
     /**
 346  
      * {@inheritDoc}
 347  
      * <p>
 348  
      * Overridden to append a JSON representation of the JavaScript element to
 349  
      * the writer provided when this object was created. This method writes the
 350  
      * elements as a
 351  
      * <code>{ $code : &lt;code&gt;, $scope : &lt;scope&gt; }</code>
 352  
      * sub-document.
 353  
      * </p>
 354  
      */
 355  
     @Override
 356  
     public void visitJavaScript(final String name, final String code,
 357  
             final Document scope) {
 358  
         try {
 359  3
             writeName(name);
 360  2
             mySink.write("{ $code : ");
 361  2
             writeQuotedString(code);
 362  2
             mySink.write(", $scope : ");
 363  2
             scope.accept(this);
 364  2
             mySink.write(" }");
 365  2
             mySink.flush();
 366  
         }
 367  1
         catch (final IOException ioe) {
 368  1
             throw new JsonException(ioe);
 369  2
         }
 370  2
     }
 371  
 
 372  
     /**
 373  
      * {@inheritDoc}
 374  
      * <p>
 375  
      * Overridden to append a JSON representation of the binary element to the
 376  
      * writer provided when this object was created. This method generates the
 377  
      * MongoDB standard NumberLong(...) JSON extension.
 378  
      * </p>
 379  
      */
 380  
     @Override
 381  
     public void visitLong(final String name, final long value) {
 382  
         try {
 383  3
             writeName(name);
 384  2
             mySink.write("NumberLong('");
 385  2
             mySink.write(Long.toString(value));
 386  2
             mySink.write("')");
 387  2
             mySink.flush();
 388  
         }
 389  1
         catch (final IOException ioe) {
 390  1
             throw new JsonException(ioe);
 391  2
         }
 392  2
     }
 393  
 
 394  
     /**
 395  
      * {@inheritDoc}
 396  
      * <p>
 397  
      * Overridden to append a JSON representation of the DBPointer element to
 398  
      * the writer provided when this object was created. This method generates
 399  
      * the non-standard MaxKey() JSON extension.
 400  
      * </p>
 401  
      */
 402  
     @Override
 403  
     public void visitMaxKey(final String name) {
 404  
         try {
 405  3
             writeName(name);
 406  2
             mySink.write("MaxKey()");
 407  2
             mySink.flush();
 408  
         }
 409  1
         catch (final IOException ioe) {
 410  1
             throw new JsonException(ioe);
 411  2
         }
 412  2
     }
 413  
 
 414  
     /**
 415  
      * {@inheritDoc}
 416  
      * <p>
 417  
      * Overridden to append a JSON representation of the DBPointer element to
 418  
      * the writer provided when this object was created. This method generates
 419  
      * the non-standard MinKey() JSON extension.
 420  
      * </p>
 421  
      */
 422  
     @Override
 423  
     public void visitMinKey(final String name) {
 424  
         try {
 425  3
             writeName(name);
 426  2
             mySink.write("MinKey()");
 427  2
             mySink.flush();
 428  
         }
 429  1
         catch (final IOException ioe) {
 430  1
             throw new JsonException(ioe);
 431  2
         }
 432  2
     }
 433  
 
 434  
     /**
 435  
      * {@inheritDoc}
 436  
      * <p>
 437  
      * Overridden to append a JSON representation of the binary element to the
 438  
      * writer provided when this object was created. This method generates the
 439  
      * MongoDB standard Timestamp(...) JSON extension.
 440  
      * </p>
 441  
      */
 442  
     @Override
 443  
     public void visitMongoTimestamp(final String name, final long value) {
 444  
         try {
 445  3
             final long time = (value >> Integer.SIZE) & 0xFFFFFFFFL;
 446  3
             final long increment = value & 0xFFFFFFFFL;
 447  
 
 448  3
             writeName(name);
 449  2
             mySink.write("Timestamp(");
 450  2
             mySink.write(Long.toString(time * 1000));
 451  2
             mySink.write(", ");
 452  2
             mySink.write(Long.toString(increment));
 453  2
             mySink.write(')');
 454  2
             mySink.flush();
 455  
         }
 456  1
         catch (final IOException ioe) {
 457  1
             throw new JsonException(ioe);
 458  2
         }
 459  2
     }
 460  
 
 461  
     /**
 462  
      * {@inheritDoc}
 463  
      * <p>
 464  
      * Overridden to append a JSON representation of the null element to the
 465  
      * writer provided when this object was created.
 466  
      * </p>
 467  
      */
 468  
     @Override
 469  
     public void visitNull(final String name) {
 470  
         try {
 471  3
             writeName(name);
 472  2
             mySink.write("null");
 473  2
             mySink.flush();
 474  
         }
 475  1
         catch (final IOException ioe) {
 476  1
             throw new JsonException(ioe);
 477  2
         }
 478  2
     }
 479  
 
 480  
     /**
 481  
      * {@inheritDoc}
 482  
      * <p>
 483  
      * Overridden to append a JSON representation of the binary element to the
 484  
      * writer provided when this object was created. This method generates the
 485  
      * MongoDB standard ObjectId(...) JSON extension.
 486  
      * </p>
 487  
      */
 488  
     @Override
 489  
     public void visitObjectId(final String name, final ObjectId id) {
 490  
         try {
 491  2453
             writeName(name);
 492  2452
             writeObjectId(id);
 493  2452
             mySink.flush();
 494  
         }
 495  1
         catch (final IOException ioe) {
 496  1
             throw new JsonException(ioe);
 497  2452
         }
 498  2452
     }
 499  
 
 500  
     /**
 501  
      * {@inheritDoc}
 502  
      * <p>
 503  
      * Overridden to append a JSON representation of the JavaScript element to
 504  
      * the writer provided when this object was created. This method writes the
 505  
      * elements as a
 506  
      * <code>{ $regex : &lt;pattern&gt;, $options : &lt;options&gt; }</code>
 507  
      * sub-document.
 508  
      * </p>
 509  
      */
 510  
     @Override
 511  
     public void visitRegularExpression(final String name, final String pattern,
 512  
             final String options) {
 513  
         try {
 514  5
             writeName(name);
 515  4
             mySink.write("{ $regex : '");
 516  4
             mySink.write(pattern);
 517  4
             if (options.isEmpty()) {
 518  2
                 mySink.write("' }");
 519  
             }
 520  
             else {
 521  2
                 mySink.write("', $options : '");
 522  2
                 mySink.write(options);
 523  2
                 mySink.write("' }");
 524  
             }
 525  4
             mySink.flush();
 526  
         }
 527  1
         catch (final IOException ioe) {
 528  1
             throw new JsonException(ioe);
 529  4
         }
 530  4
     }
 531  
 
 532  
     /**
 533  
      * {@inheritDoc}
 534  
      * <p>
 535  
      * Overridden to append a JSON representation of the string element to the
 536  
      * writer provided when this object was created.
 537  
      * </p>
 538  
      */
 539  
     @Override
 540  
     public void visitString(final String name, final String value) {
 541  
         try {
 542  4629
             writeName(name);
 543  4628
             writeQuotedString(value);
 544  4628
             mySink.flush();
 545  
         }
 546  1
         catch (final IOException ioe) {
 547  1
             throw new JsonException(ioe);
 548  4628
         }
 549  4628
     }
 550  
 
 551  
     /**
 552  
      * {@inheritDoc}
 553  
      * <p>
 554  
      * Overridden to append a JSON representation of the symbol element to the
 555  
      * writer provided when this object was created.
 556  
      * </p>
 557  
      */
 558  
     @Override
 559  
     public void visitSymbol(final String name, final String symbol) {
 560  
         try {
 561  10
             writeName(name);
 562  9
             if (SYMBOL_PATTERN.matcher(symbol).matches()) {
 563  7
                 mySink.write(symbol);
 564  
             }
 565  
             else {
 566  2
                 writeQuotedString(symbol);
 567  
             }
 568  9
             mySink.flush();
 569  
         }
 570  1
         catch (final IOException ioe) {
 571  1
             throw new JsonException(ioe);
 572  9
         }
 573  
 
 574  9
     }
 575  
 
 576  
     /**
 577  
      * {@inheritDoc}
 578  
      * <p>
 579  
      * Overridden to append a JSON representation of the binary element to the
 580  
      * writer provided when this object was created. This method generates the
 581  
      * MongoDB standard ISODate(...) JSON extension.
 582  
      * </p>
 583  
      */
 584  
     @Override
 585  
     public void visitTimestamp(final String name, final long timestamp) {
 586  4
         final SimpleDateFormat sdf = new SimpleDateFormat(
 587  
                 "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
 588  4
         sdf.setTimeZone(UTC);
 589  
 
 590  
         try {
 591  4
             writeName(name);
 592  3
             mySink.write("ISODate('");
 593  3
             mySink.write(sdf.format(new Date(timestamp)));
 594  3
             mySink.write("')");
 595  3
             mySink.flush();
 596  
         }
 597  1
         catch (final IOException ioe) {
 598  1
             throw new JsonException(ioe);
 599  3
         }
 600  
 
 601  3
     }
 602  
 
 603  
     /**
 604  
      * Returns if the visitor is currently suppressing the names of elements.
 605  
      * This is true when serializing an array.
 606  
      * 
 607  
      * @return If the visitor is currently suppressing the names of elements.
 608  
      *         This is true when serializing an array.
 609  
      */
 610  
     protected boolean isSuppressNames() {
 611  0
         return mySuppressNames;
 612  
     }
 613  
 
 614  
     /**
 615  
      * Writes a new line if {@link #myOneLine} is false and indents to the
 616  
      * {@link #myIndentLevel}.
 617  
      * 
 618  
      * @throws IOException
 619  
      *             On a failure to write the new line.
 620  
      */
 621  
     protected void nl() throws IOException {
 622  22417
         if (!myOneLine) {
 623  22405
             mySink.write(NL);
 624  65436
             for (int i = 0; i < myIndentLevel; ++i) {
 625  43031
                 mySink.write("  ");
 626  
             }
 627  
         }
 628  
         else {
 629  12
             mySink.write(' ');
 630  
         }
 631  22417
     }
 632  
 
 633  
     /**
 634  
      * Sets the value for if the visitor is currently suppressing the names of
 635  
      * elements. This is true, for example, when serializing an array.
 636  
      * 
 637  
      * @param suppressNames
 638  
      *            The new value for if names should be suppressed.
 639  
      */
 640  
     protected void setSuppressNames(final boolean suppressNames) {
 641  17
         mySuppressNames = suppressNames;
 642  17
     }
 643  
 
 644  
     /**
 645  
      * Writes the name if {@link #mySuppressNames} is false.
 646  
      * 
 647  
      * @param name
 648  
      *            The name to write, if not suppressed.
 649  
      * @throws IOException
 650  
      *             On a failure to write the new line.
 651  
      */
 652  
     protected void writeName(final String name) throws IOException {
 653  18888
         if (!mySuppressNames) {
 654  16576
             if (SYMBOL_PATTERN.matcher(name).matches()) {
 655  13796
                 mySink.write(name);
 656  
             }
 657  
             else {
 658  2780
                 writeQuotedString(name);
 659  
             }
 660  16557
             mySink.write(" : ");
 661  
         }
 662  18869
     }
 663  
 
 664  
     /**
 665  
      * Writes the {@link ObjectId}.
 666  
      * 
 667  
      * @param id
 668  
      *            The {@link ObjectId} to write.
 669  
      * @throws IOException
 670  
      *             On a failure writing to the sink.
 671  
      */
 672  
     protected void writeObjectId(final ObjectId id) throws IOException {
 673  2454
         mySink.write("ObjectId('");
 674  
 
 675  2454
         String hex = Integer.toHexString(id.getTimestamp());
 676  2454
         mySink.write("00000000".substring(hex.length()));
 677  2454
         mySink.write(hex);
 678  
 
 679  2454
         hex = Long.toHexString(id.getMachineId());
 680  2454
         mySink.write("0000000000000000".substring(hex.length()));
 681  2454
         mySink.write(hex);
 682  
 
 683  2454
         mySink.write("')");
 684  2454
     }
 685  
 
 686  
     /**
 687  
      * Writes the {@code string} as a quoted string.
 688  
      * 
 689  
      * @param string
 690  
      *            The String to write.
 691  
      * @throws IOException
 692  
      *             On a failure writing the String.
 693  
      */
 694  
     protected void writeQuotedString(final String string) throws IOException {
 695  7418
         if (string.indexOf('\'') < 0) {
 696  7416
             mySink.write('\'');
 697  7416
             mySink.write(string);
 698  7416
             mySink.write('\'');
 699  
         }
 700  2
         else if (string.indexOf('"') < 0) {
 701  1
             mySink.write('"');
 702  1
             mySink.write(string);
 703  1
             mySink.write('"');
 704  
         }
 705  
         else {
 706  1
             mySink.write('\'');
 707  
             // Escape any embedded single quotes.
 708  1
             mySink.write(string.replaceAll("'", "\\\\'"));
 709  1
             mySink.write('\'');
 710  
         }
 711  7418
     }
 712  
 }