package com.blubb.gyingpan; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.RandomAccessFile; import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import com.blubb.gyingpan.actions.ChangeParentsAction; import com.blubb.gyingpan.actions.RenameAction; import com.blubb.gyingpan.actions.TrashAction; import com.google.api.client.googleapis.media.MediaHttpUploader; import com.google.api.client.googleapis.media.MediaHttpUploaderProgressListener; import com.google.api.client.http.FileContent; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpResponse; import com.google.api.client.util.DateTime; import com.google.api.services.drive.Drive.Files.Insert; import com.google.api.services.drive.model.File; import com.google.api.services.drive.model.ParentReference; public class Node implements Serializable { private static final long serialVersionUID = 2L; static final String folderType = "application/vnd.google-apps.folder" .intern(); String name; String id; String mimetype; long lastModified; long size; String etag; String openurl; List<Node> children = null; CacheStatus cached = CacheStatus.NotInCache; transient GDrive gd; ArrayList<Node> parents = new ArrayList<Node>(); Node(String name, String id, String mimetype, long lastModified, long size, String etag, String openurl, GDrive drive) { this.gd = drive; this.name = name; this.id = id; this.mimetype = mimetype.intern(); if (this.mimetype == folderType) children = new LinkedList<Node>(); this.lastModified = lastModified; this.size = size; this.etag = etag; if (isGoogleFile()) { System.out.println(openurl); this.openurl = openurl; } } public boolean isDirectory() { return mimetype == folderType; } public boolean isGoogleFile() { if (mimetype == folderType) return false; return mimetype.startsWith("application/vnd.google-apps"); } public String getFileName() { if (isGoogleFile()) return name + ".html"; return name; } private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { inputStream.defaultReadObject(); mimetype = mimetype.intern(); } public long getSize() { if (isGoogleFile()) return getRedirFile().getBytes(StandardCharsets.UTF_8).length; return size; } String getRedirFile() { String link = "<html>\n" + "<head>\n" + "<title>redirect</title>\n" + "<script>\n" + "window.location.href=\"" + openurl + "\";\n" + "</script>\n" + "</head>\n" + "<body>\n" + "</body>\n" + "</html>"; return link; } public void setName(String name) { this.name = name; } String getPath() { // if (parents.isEmpty()) return ""; // return parents.get(0).getPath() + "/" + getFileName(); } public synchronized java.io.File cache() throws IOException { synchronized (this) { //System.out.println("cache " + id + " " + isDirectory()); if (isDirectory()) return null; java.io.File cachefile = cacheFile(); if (cached == CacheStatus.Dirty) return cachefile; if (cached == CacheStatus.InCache) { if (cachefile.exists()) { if (cachefile.length() == getSize() && Math.abs(cachefile.lastModified() - lastModified) < 1500) return cachefile; else { System.out.println("deleting old cachefile " + id + " cf.lm " + cachefile.lastModified() + " n.lm " + lastModified); cachefile.delete(); cached = CacheStatus.NotInCache; } } } if (isGoogleFile()) { FileOutputStream fos = new FileOutputStream(cachefile); fos.write(getRedirFile().getBytes(StandardCharsets.UTF_8)); fos.close(); cachefile.setLastModified(lastModified); cached = CacheStatus.InCache; return cachefile; } if (cachefile.exists()) { if (cachefile.length() == getSize() && Math.abs(cachefile.lastModified() - lastModified) < 1500) { cached = CacheStatus.InCache; return cachefile; } } String url = gd.service.files().get(id).execute().getDownloadUrl(); GYMain.setStatus(url); HttpResponse resp = gd.service.getRequestFactory() .buildGetRequest(new GenericUrl(url)).execute(); FileOutputStream fos = new FileOutputStream(cachefile); resp.download(fos); fos.close(); cachefile.setLastModified(lastModified); cached = CacheStatus.InCache; return cachefile; } } java.io.File cacheFile() { java.io.File parentDir = new java.io.File(gd.cachedir, Integer.toHexString(id.hashCode() & 0xFF)); java.io.File f = new java.io.File(parentDir, id); return f; } public synchronized void markDirty() { lastModified = System.currentTimeMillis(); if (cached != CacheStatus.Dirty) { cached = CacheStatus.Dirty; gd.addToDirtyNodes(this); } } public synchronized void truncate(long len) throws IOException { RandomAccessFile ra = new RandomAccessFile(cacheFile(), "rw"); ra.setLength(len); ra.close(); size = len; } public synchronized void flush() { GYMain.setStatus("flushing " + id + " " + getPath()); if (cached != CacheStatus.Dirty) return; if (id.startsWith("tobefilled-")) { File newFile = new File(); String mimeType = "application/octet-stream"; newFile.setMimeType(mimeType); newFile.setModifiedDate(new DateTime(lastModified)); newFile.setTitle(name); ArrayList<ParentReference> prefs = new ArrayList<ParentReference>(); for (Node p : parents) { prefs.add(new ParentReference().setId(p.id)); } newFile.setParents(prefs); FileContent mediaContent = new FileContent(mimeType, cacheFile()); try { Insert insert = gd.service.files().insert(newFile, mediaContent); long startTime = System.currentTimeMillis(); if (insert.getMediaHttpUploader() != null) insert.getMediaHttpUploader().setProgressListener( new MediaHttpUploaderProgressListener() { @Override public void progressChanged( MediaHttpUploader uploader) throws IOException { if(uploader.getProgress() > 0.0001) { long now = System.currentTimeMillis(); double eta = (now-startTime)/uploader.getProgress()-(now-startTime); GYMain.setStatus(String.format("%s progress %.2f%% (eta %ds)\n", name, uploader.getProgress()*100, (int)eta/1000)); } } }); File f = insert.execute(); java.io.File oldCacheFile = cacheFile(); id = f.getId(); GYMain.setStatus("rename " + oldCacheFile + " to " + cacheFile()); GYMain.setStatus("success: " + oldCacheFile.renameTo(cacheFile())); cached = CacheStatus.InCache; } catch (IOException e) { e.printStackTrace(); } } else { try { File f = gd.service.files().get(id).execute(); f.setModifiedDate(new DateTime(lastModified)); FileContent mediaContent = new FileContent(mimetype, cacheFile()); f = gd.service.files().update(id, f, mediaContent) .setSetModifiedDate(true).setNewRevision(true) .execute(); cached = CacheStatus.InCache; } catch (IOException e) { e.printStackTrace(); } } gd.requestPersist(); } public void rename(String toName) { GYMain.setStatus("rename " + getPath() + " " + toName); synchronized (this) { if (cached != CacheStatus.Dirty) { gd.addAction(new RenameAction(id, toName)); } setName(toName); } } public void move(Node oldParent, Node toParent) { GYMain.setStatus("move " + getPath() + " " + toParent.getPath()); synchronized (this) { if (!id.startsWith("tobefilled-")) { gd.addAction(new ChangeParentsAction(id, oldParent.id, toParent.id)); } synchronized (gd) { oldParent.children.remove(this); toParent.children.add(this); parents.remove(oldParent); parents.add(toParent); } } } public synchronized void delete(Node parent) { parent.children.remove(this); parents.remove(parent); if (!id.startsWith("tobefilled-")) { gd.addAction(new ChangeParentsAction(id, parent.id, null)); if(parents.isEmpty()) gd.addAction(new TrashAction(id)); } } }