package org.vaadin.mideaas.editor; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.commons.io.FileUtils; import org.vaadin.aceeditor.client.AceDoc; import org.vaadin.mideaas.editor.DocDiffMediator.Guard; public class MultiUserDoc implements SharedDoc.Listener { public interface DifferingChangedListener { public void differingChanged(Map<EditorUser, DocDifference> diffs); } private final Guard guard; private final File saveBaseTo; private final SharedDoc base; private final HashMap<EditorUser, UserDoc> userDocs = new HashMap<EditorUser, UserDoc>(); private final CopyOnWriteArrayList<DifferingChangedListener> dcListeners = new CopyOnWriteArrayList<DifferingChangedListener>(); // ... private final Timer baseChangeTimer = new Timer(); private boolean fireScheduled = false; public MultiUserDoc(AceDoc initial, File saveBaseTo, Guard guard) { this.saveBaseTo = saveBaseTo; this.guard = guard; base = new SharedDoc(initial); base.addListener(this); } public SharedDoc getBase() { return base; } public synchronized UserDoc getUserDoc(EditorUser user) { UserDoc ud = userDocs.get(user); if (ud==null) { ud = createUserDoc(user); userDocs.put(user, ud); } return ud; } public synchronized void removeUserDoc(EditorUser user) { UserDoc ud = userDocs.remove(user); if (ud!=null) { ud.getDoc().removeListener(this); ud.getMed().detach(); } } public synchronized Map<EditorUser, DocDifference> getDifferences() { HashMap<EditorUser, DocDifference> diffs = new HashMap<EditorUser, DocDifference>(); for (UserDoc ud : userDocs.values()) { DocDifference dd = ud.getDiff(); if (dd.isChanged()) { diffs.put(ud.getUser(), dd); } } return diffs; } private UserDoc createUserDoc(EditorUser user) { SharedDoc doc = new SharedDoc(getBase().getDoc()); DocDiffMediator med = new DocDiffMediator(base, doc); med.setUpwardsGuard(guard); UserDoc ud = new UserDoc(user, doc, med, base); doc.addListener(this); return ud; } @Override public void changed() { // Delaying a bit. Not acting on each change, only after a while. // This is a bit so so... synchronized (baseChangeTimer) { if (fireScheduled) { return; } baseChangeTimer.schedule(new TimerTask() { @Override public void run() { synchronized (baseChangeTimer) { if (saveBaseTo!=null) { saveBaseToDisk(); } fireDifferingChanged(getDifferences()); fireScheduled = false; } } }, 400); fireScheduled = true; } } protected void saveBaseToDisk() { try { FileUtils.write(saveBaseTo, getBaseText()); } catch (IOException e) { System.err.println("WARNING: could not save to "+saveBaseTo); } } public synchronized void addDifferingChangedListener(DifferingChangedListener li) { dcListeners.add(li); } public synchronized void removeDifferingChangedListener(DifferingChangedListener li) { dcListeners.remove(li); } private void fireDifferingChanged(Map<EditorUser, DocDifference> diffs) { for (DifferingChangedListener li : dcListeners) { li.differingChanged(diffs); } } public String getBaseText() { return base.getDoc().getText(); } public void setBaseNoFire(String xml) { base.setDoc(new AceDoc(xml)); } }