@@ -0,0 +1,2938 @@
+package com.google.sps.workspace;%0A%0Aimport java.util.List;%0Aimport java.util.concurrent.CompletableFuture;%0Aimport java.util.concurrent.Future;%0A%0Aimport com.google.api.client.util.Data;%0Aimport com.google.firebase.database.DataSnapshot;%0Aimport com.google.firebase.database.DatabaseError;%0Aimport com.google.firebase.database.Query;%0Aimport com.google.firebase.database.ValueEventListener;%0A%0Apublic class WorkspaceFile %7B%0A  private final Query q;%0A  private final String docBase;%0A%0A  private final String fileName;%0A%0A  protected WorkspaceFile(DataSnapshot snap) %7B%0A    // TODO: decode fileName%0A    fileName = snap.getKey();%0A    if (snap.hasChild(%22checkpoint/id%22)) %7B%0A      docBase = (String) snap.child(%22checkpoint%22).child(%22o%22).getValue();%0A%0A      q = snap.getRef().child(%22history%22).orderByKey().startAt((String) snap.child(%22checkpint%22).child(%22id%22).getValue());%0A    %7D else %7B%0A      docBase = %22%22;%0A%0A      q = snap.getRef().child(%22history%22).orderByKey();%0A    %7D%0A  %7D%0A%0A%0A  /**%0A   * Reconstructs a file from its edit history.%0A   * %0A   * Modified from firepad ruby example:%0A   * https://github.com/FirebaseExtended/firepad/blob/master/examples/firepad.rb%0A   * %0A   * @return A Future%3CString%3E which will contain the file contents.%0A   */%0A  public Future%3CString%3E getContents() %7B%0A    CompletableFuture%3CString%3E future = new CompletableFuture%3C%3E();%0A%0A    q.addListenerForSingleValueEvent(new ValueEventListener()%7B%0A    %0A      @Override%0A      public void onDataChange(DataSnapshot snapshot) %7B%0A        String doc = padSurrogatePairs(docBase);%0A        for (DataSnapshot ops : snapshot.getChildren()) %7B%0A          long idx = 0;%0A          for (DataSnapshot op : (List%3CDataSnapshot%3E) ops.getValue()) %7B%0A            if (op.getValue() instanceof Long) %7B%0A              long longOp = op.getValue(Long.class).longValue();%0A              if (longOp %3E 0) %7B%0A                // retain%0A                idx += longOp;%0A              %7D else %7B%0A                // delete%0A                doc = doc.substring(0, (int) idx) + doc.substring((int) (idx - longOp));%0A              %7D%0A            %7D else %7B%0A              // insert%0A              String stringOp = padSurrogatePairs(op.getValue(String.class));%0A              doc = doc.substring(0, (int) idx+1) + stringOp + doc.substring((int) idx + 1);%0A            %7D%0A          %7D%0A%0A          // Remove surrogate pair padding.%0A          doc.replaceAll(%22%5C0%22, %22%22);%0A        %7D%0A        %0A        future.complete(doc);%0A      %7D%0A    %0A      @Override%0A      public void onCancelled(DatabaseError error) %7B%0A        future.completeExceptionally(error.toException());%0A      %7D%0A    %7D);%0A%0A    return future;%0A  %7D%0A%0A  public static String padSurrogatePairs(String str) %7B%0A    String newStr = str;%0A%0A    int offset = 0;%0A    for (int i = 0; i %3C str.length(); i++) %7B%0A      if (str.codePointAt(i) %3E= 0x10000 && str.codePointAt(i) %3C= 0x10FFFF) %7B%0A        newStr = newStr.substring(0, i+offset+1) + '%5C0' + newStr.substring(i+offset+1);%0A        offset++;%0A      %7D%0A    %7D%0A%0A    return newStr;%0A  %7D%0A%7D

Add workspaceFile class which will reconstruct files from their history.