/** File Synchronization Program: History: 2002.1.10 ディレクトリ階層のコピー 2001.12.30 シンクしたフォルダーの履歴を残すようにした **/ import javax.swing.*; import javax.swing.event.*; import javax.swing.tree.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; /** Syncのユーザ設定 **/ class SyncOptions { boolean valid = true; String name; File left; File right; String ignore; String[] ilist; boolean force; static boolean case_sensitive; long terror = 0; String synchist[][]; boolean debug = false; boolean dryrun = false; boolean dolog = true; public SyncOptions(){ left = new File("C:"); right = new File("C:"); synchist = new String[20][2]; // 20: history size } public SyncOptions(String fname) { this(); load(fname); } public SyncOptions copy() { SyncOptions o = new SyncOptions(); o.valid = valid; o.left = left; o.right = right; o.ignore = ignore; o.ilist = null; o.force = force; o.case_sensitive = case_sensitive; o.debug = debug; o.dryrun = dryrun; o.dolog = dolog; o.terror = terror; for (int i = 0; i < synchist.length; i++) { o.synchist[i][0] = synchist[i][0]; o.synchist[i][1] = synchist[i][1]; } return o; } /** 無視すべきファイル名リストに合致するかチェック * @return true:無視すべき名前のとき */ public boolean checkIgnore(String s) { if (ignore == null || s == null) return false; if (ilist == null) { try { StringTokenizer tk = new StringTokenizer(ignore, ";"); ilist = new String[tk.countTokens()]; for (int i = 0; i < ilist.length; i++) { ilist[i] = tk.nextToken(); Sync.dbgout("token"+i + ": " + ilist[i]); //XXXX } } catch (Exception ex) {} } for (int i = 0; i < ilist.length; i++) { if (ilist[i] == null) continue; if (ilist[i].equals(s)) { return true; } else if (ilist[i].startsWith("*")) { if (s.endsWith(ilist[i].substring(1, ilist[i].length()))) return true; } else if (ilist[i].endsWith("*")) { if (s.startsWith(ilist[i].substring(0, ilist[i].length()-1))) return true; } } return false; } public void save(String fname) { checkIgnore("aa");//XXXX try { PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fname))); out.println("%name " + name); out.println("%dir1 " + left.getPath()); out.println("%dir2 " + right.getPath()); if (ignore != null) out.println("%igno " + ignore); out.println("%forc " + force); out.println("%case_sensitive " + case_sensitive); out.println("%debug " + debug); out.println("%dryrun " + dryrun); out.println("%terr " + terror); out.println("%dolog " + dolog); // sync pair history for (int i = synchist.length-1; i >= 0; i--) { if (!is_empty(i)) { out.println("%hdir1 " + synchist[i][0]); out.println("%hdir2 " + synchist[i][1]); } } out.close(); } catch (Exception ex) {System.out.println(""+ex);} } public boolean is_empty(int i) { return (synchist[i][0] == null || synchist[i][1] == null || synchist[i][0].length() <= 0 || synchist[i][1].length() <= 0); } public void load(String fname) { try { BufferedReader in = new BufferedReader(new FileReader(fname)); String l = null; while ((l = in.readLine()) != null) { if (l.startsWith("%name ")) { name = l.substring(6, l.length()); } else if (l.startsWith("%dir1 ")) { left = new File(l.substring(6, l.length())); } else if (l.startsWith("%dir2 ")) { right = new File(l.substring(6, l.length())); } else if (l.startsWith("%forc ")) { if (l.endsWith("true")) force = true; } else if (l.startsWith("%case_sensitive ")) { case_sensitive = l.endsWith("true"); } else if (l.startsWith("%debug ")) { debug = l.endsWith("true"); } else if (l.startsWith("%dryrun ")) { dryrun = l.endsWith("true"); } else if (l.startsWith("%dolog ")) { dolog = l.endsWith("true"); } else if (l.startsWith("%igno ")) { ignore = l.substring(6, l.length()); } else if (l.startsWith("%terr ")) { terror = Long.parseLong(l.substring(6, l.length())); } else if (l.startsWith("%hdir1 ")) { String s1 = l.substring(7, l.length()); l = in.readLine(); if (l.startsWith("%hdir2 ")) { String s2 = l.substring(7, l.length()); addHistory(s1, s2); } } } in.close(); } catch (Exception ex) {System.out.println(""+ex);} } /** 履歴に追加 **/ public void addHistory(String s1, String s2) { //System.out.println("addHistory " + s1 + "," + s2);//XXXX if (s1 == null || s1.length() <= 0 || s2 == null || s2.length() <= 0) return; for (int i = 0; i < synchist.length; i++) { if (synchist[i][0] != null && synchist[i][1] != null && synchist[i][0].equals(s1) && synchist[i][1].equals(s2)) return; // already listed } for (int i = synchist.length-1; i > 0; i--) { synchist[i][0] = synchist[i-1][0]; synchist[i][1] = synchist[i-1][1]; } synchist[0][0] = s1; synchist[0][1] = s2; //System.out.println("addHistory " + numHistory() + " " + s1 + "==" + s2); } public int numHistory() { int n = 0; for (int i = 0; i < synchist.length; i++) { if (!is_empty(i)) n++; } return n; } } /** SyncOptions を変更するためのダイアローグボックス **/ class OptionDialog extends JDialog implements ActionListener { Container pane; JButton can_b, ok_b, left_b, right_b; JComboBox set_cb; JTextField ignore_tf, left_tf, right_tf, terror_tf; JCheckBox force_cb, case_cb, debug_cb, dryrun_cb, dolog_cb; SyncOptions opt; JComboBox history_cb; String history[][]; String hist_menu[]; public JButton addButton(Container pane, String title) { JButton btn = new JButton(title); btn.addActionListener(this); if (pane != null) pane.add(btn); return btn; } /** 履歴ComboBoxをopt.synchistにあわせて更新 **/ public void updateHistMenu() { history_cb.removeAllItems(); int n = opt.numHistory(); //System.out.println("history length = " + n);//XXX if (n <= 0) return; history = new String[n][2]; hist_menu = new String[n]; int j = 0; //System.out.println("updateHistMenu " + opt.synchist.length);//XXX for (int i = 0; i < opt.synchist.length; i++) { if (!opt.is_empty(i)) { try { history[j][0] = opt.synchist[i][0]; history[j][1] = opt.synchist[i][1]; hist_menu[j] = history[j][0] + " - " + history[j][1]; j++; } catch (Exception e) { Sync.dbgout("updateHist " + e + "i=" + i + " j=" + j); } } } history_cb.setEditable(true); for (int i = hist_menu.length-1; i >= 0; i--) { history_cb.addItem(hist_menu[i]); Sync.dbgout("addMenu " + i + " " + hist_menu[i]);//XXX } history_cb.setEditable(false); } public void selectFromHistory(String s) { for (int i = 0; i < hist_menu.length; i++) { if (s.equals(hist_menu[i])) { left_tf.setText(history[i][0]); right_tf.setText(history[i][1]); opt.left = new File(left_tf.getText()); opt.right = new File(right_tf.getText()); return; } } } public void actionPerformed(ActionEvent e) { Object o = e.getSource(); if (o == ok_b) { opt.valid = true; // widgetの設定をoptに反映させる //opt.name = set_cb.getText(); opt.ignore = ignore_tf.getText(); opt.force = force_cb.getSelectedObjects() != null; opt.left = new File(left_tf.getText()); opt.right = new File(right_tf.getText()); opt.ilist = null; opt.debug = debug_cb.getSelectedObjects() != null; opt.dryrun = dryrun_cb.getSelectedObjects() != null; opt.dolog = dolog_cb.getSelectedObjects() != null; opt.addHistory(left_tf.getText(), right_tf.getText()); try { opt.terror = Long.parseLong(terror_tf.getText()) * 1000L; } catch (Exception ex) {} dispose(); } else if (o == can_b) { opt.valid = false; dispose(); } else if (o == left_b) { File f = Sync.chooseDirectory(this); if (f != null) left_tf.setText(f.getPath()); } else if (o == right_b) { File f = Sync.chooseDirectory(this); if (f != null) right_tf.setText(f.getPath()); } else if (o == history_cb) { Sync.dbgout("history_cb pressed"); String s = (String)history_cb.getSelectedItem(); selectFromHistory(s); } } public static JPanel addRowPanel(JPanel parent) { JPanel cp = new JPanel(); cp.setLayout(new FlowLayout(FlowLayout.LEFT, 3, 2)); if (parent != null) parent.add(cp); return cp; } public OptionDialog(JFrame owner, String title, SyncOptions opt) { super(owner, title, true); // modal dialog this.opt = opt; pane = getContentPane(); pane.setLayout(new BorderLayout()); JPanel center = new JPanel(); center.setLayout(new GridLayout(0,1)); /* JPanel cp = addRowPanel(center); cp.add(new JLabel("Settings Name:")); String scom[] = {"eDesk.new", "Sync", "My Documents"}; set_cb = new JComboBox(scom); cp.add(set_cb); */ JPanel cp = addRowPanel(center); cp.add(new JLabel("Folder1:")); left_tf = new JTextField(opt.left.getPath(), 20); cp.add(left_tf); left_b = addButton(cp, "参照..."); cp = addRowPanel(center); cp.add(new JLabel("Folder2:")); right_tf = new JTextField(opt.right.getPath(), 20); cp.add(right_tf); right_b = addButton(cp, "参照..."); cp = addRowPanel(center); cp.add(new JLabel("History:")); String dummy[] = {"dummy"}; history_cb = new JComboBox(dummy); updateHistMenu(); history_cb.addActionListener(this); cp.add(history_cb); cp = addRowPanel(center); cp.add(new JLabel("IgnoreFiles:")); ignore_tf = new JTextField(opt.ignore, 25); cp.add(ignore_tf); cp = addRowPanel(center); force_cb = new JCheckBox("無変更のファイルもリストする", opt.force); cp.add(force_cb); cp = addRowPanel(center); case_cb = new JCheckBox("ファイル名の大文字・小文字を区別する", opt.case_sensitive); cp.add(case_cb); cp = addRowPanel(center); cp.add(new JLabel("時間誤差")); terror_tf = new JTextField(""+(opt.terror / 1000), 4); cp.add(terror_tf); cp.add(new JLabel("秒まで許容")); cp = addRowPanel(center); debug_cb = new JCheckBox("Debug out", opt.debug); dryrun_cb = new JCheckBox("Dryrun", opt.dryrun); dolog_cb = new JCheckBox("Log (log.txt)を残す", opt.dolog); cp.add(debug_cb); cp.add(dryrun_cb); cp.add(dolog_cb); JPanel bottom = new JPanel(); bottom.setLayout(new FlowLayout(FlowLayout.RIGHT, 3, 4)); can_b = addButton(bottom, "Cancel"); ok_b = addButton(bottom, "OK"); pane.add(center, BorderLayout.CENTER); pane.add(bottom, BorderLayout.SOUTH); this.pack(); Sync.dbgout("option dialog"); } } /** コピーすべきファイルの対。 * TreeNode に対応している。 **/ class FilePair { boolean leaf; // f1, f2 are files? File f1, f2, dir1, dir2; int action; String label = " ?? "; static final int NON = 0; static final int L2R = 1; static final int R2L = 2; static final int EQ = 3; static final String[] labels = {" ??? ", " --> ", " <-- ", " === "}; public FilePair(File dir1, File f1, File dir2, File f2, boolean leaf, int action) { this.f1 = f1; this.f2 = f2; this.dir1 = dir1; this.dir2 = dir2; if (f1 != null) this.f1 = new File(dir1, f1.getName()); if (f2 != null) this.f2 = new File(dir2, f2.getName()); this.action = action; this.leaf = leaf; updateLabel(); } public DefaultMutableTreeNode makeNode() { return new DefaultMutableTreeNode(this); } public void updateLabelOld() { try { String s1 = (f1 != null) ? (f1.isDirectory() ? f1.getPath() : f1.getName()) : " "; String s2 = (f2 != null) ? (f2.isDirectory() ? f2.getPath() : f2.getName()) : " "; label = s1 + labels[action] + s2; } catch (Exception e) { label = "*"+e;} } public void updateLabel() { try { String s1 = (f1 != null) ? f1.getName() : ""; String s2 = (f2 != null) ? f2.getName() : ""; label = s1 + labels[action] + s2; } catch (Exception e) { label = "*"+e;} } public String toString() { return label; } public static String q(String s) { return "\"" + s + "\""; } /* obsolete */ /* public String comLine() { if (f1 == null && f2 == null) { return null; } else if (f1 != null && f2 == null) { if (dir2 != null && dir2.isDirectory() && action == L2R) { if (f1.isDirectory()) return "mkdir " + q(dir2.getPath() + "\\" + f1.getName()); else return "copy " + q(f1.getPath()) + " " + q(dir2.getPath()); } else { System.out.println("error1 " + f1 + " (" + dir2 + ")"); return null; } } else if (f1 == null && f2 != null) { if (dir1 != null && dir1.isDirectory() && action == R2L) { if (f2.isDirectory()) return "mkdir " + q(dir1.getPath() + "\\" + f2.getName()); else return "copy " + q(f2.getPath()) + " " + q(dir1.getPath()); } else { System.out.println("error2 (" + dir1 + ") " + f2); return null; } } else { if (f1.isDirectory() && f2.isDirectory()) { return null; } else if (f1.isDirectory() != f2.isDirectory()) { System.out.println("error3 " + f1 + " " + f2); return null; } else { // both are files switch (action) { case L2R: return "copy " + q(f1.getPath()) + " " + q(f2.getPath()); case R2L: return "copy " + q(f2.getPath()) + " " + q(f1.getPath()); default: return null; } } } } */ public boolean doAction(boolean dryrun, PrintWriter log) { /* if (dryrun) try { System.out.println("# " + dir1.getPath()+"/" + (f1 != null ? f1.getName() : "null") + labels[action] + dir2.getPath() + "/" + (f2 != null ? f2.getName() : "null") ); return true; } catch (Exception e) { Sync.dbgout("doAction:" + e); return true; } */ if (f1 == null && f2 == null) { return false; } else if (f1 != null && f2 == null) { if (dir2 != null && dir2.isDirectory() && action == L2R) return gcopy(f1, dir2, dryrun,log); } else if (f1 == null && f2 != null) { if (dir1 != null && dir1.isDirectory() && action == R2L) return gcopy(f2, dir1, dryrun,log); } else { if (f1.isDirectory() && f2.isDirectory()) { return true; } else if (f1.isDirectory() != f2.isDirectory()) { System.out.println("error3 " + f1 + " " + f2); return false; } else { // both are files switch (action) { case L2R: return gcopy(f1, f2, dryrun, log); case R2L: return gcopy(f2, f1, dryrun, log); } } } return true; } private static final int BUFSIZE = 65536; /** from からtoへファイルをコピーする。toはすでに存在するファイルでもよい。 * toはディレクトリでもよい(そのディレクトリの下にfromと同名のファイルへ * コピーする。 * (File#renameToがあるのにFile#copyが存在しないので自前で作った) * * @return true - コピー成功, false - 失敗 **/ public static boolean copy(File from, File to) { BufferedInputStream in = null; BufferedOutputStream out = null; boolean result = true; //Sync.dbgout("copy: " + from + " " + to);//XXXX //try {Thread.sleep(500);} catch (Exception ex){} try { if (!from.canRead() || from.isDirectory()) return false; if (to.isDirectory()) to = new File(to, from.getName()); long t = from.lastModified(); in = new BufferedInputStream(new FileInputStream(from)); out = new BufferedOutputStream(new FileOutputStream(to)); byte[] buf = new byte[BUFSIZE]; for (long len = from.length(); len > 0; ) { int n = in.read(buf, 0, (int)(len > BUFSIZE ? BUFSIZE : len)); if (n < 0) break; out.write(buf, 0, n); len -= n; } out.close(); to.setLastModified(t); // 更新時刻を元ファイルと同じにする } catch (Exception e) { System.out.println("fileCopy: " + from + "-->" + to); System.out.println(e.getMessage()); Sync.error_message = "" + from+ "を" + to + "に転送中に以下のエラー発生:\n" + e.getMessage(); result = false; } try {in.close();} catch (Exception ex){} try {out.close();} catch (Exception ex){} return result; } /** generic copy from がディレクトリのときはto以下にmkdirする。 **/ public static boolean gcopy(File from, File to, boolean dryrun, PrintWriter log) { if (from.isDirectory() && to.isDirectory()) { // mkdir return mkdir(new File(to, from.getName()), dryrun, log); } if (log != null) { log.println("copy " + from + " " + to); } if (dryrun) { Sync.dbgout("#copy " + from + "-->" + to); return true; } return copy(from, to); } public static boolean mkdir(File newdir, boolean dryrun, PrintWriter log) { if (log != null) { log.println("mkdir " + newdir); } if (dryrun) { Sync.dbgout("#mkdir " + newdir); return true; } return newdir.mkdir(); } } /** * Sync -- ファイル同期ツール * @version $Id: Sync.java,v 1.1 1999/01/02 03:35:30 user Exp $ */ public class Sync extends JPanel implements ActionListener, Runnable { SyncOptions opt; JPanel pane; JButton left_b, right_b, dir_b, prescan_b, start_b, stop_b; Color dir_back; JButton rem_b, option_b; JProgressBar pbar; JTree tree; JScrollPane spane; JLabel info_l; int direction = BOTH; // 0, 1, 2; Icon left_i, right_i, both_i; DefaultMutableTreeNode top; DefaultTreeModel top_m; JFrame frame; boolean stop_copy = false; boolean stop_copy_confirmed = false; int job_type = 0; public static final int PRESCAN = 1; public static final int DOSYNC = 2; public static final int LEFT = 0x2; public static final int BOTH = 0x3; public static final int RIGHT = 0x1; public void message(String s) { info_l.setText(s); } private void needRescan() { pbar.setValue(0); start_b.setEnabled(false); message("press 'Prescan' to refresh the list."); } public void actionPerformed(ActionEvent e) { Object o = e.getSource(); if (o == dir_b) { switch (direction) { case LEFT: direction = BOTH; dir_b.setIcon(both_i); break; case BOTH: direction = RIGHT; dir_b.setIcon(right_i); break; case RIGHT: direction = LEFT; dir_b.setIcon(left_i); break; } needRescan(); } else if (o == left_b) { File f = chooseDirectory(this); if (f != null) { opt.left = f; left_b.setText(f.getPath()); needRescan(); } } else if (o == right_b) { File f = chooseDirectory(this); if (f != null) { opt.right = f; right_b.setText(f.getPath()); needRescan(); } } else if (o == prescan_b) { if (opt.left == null || opt.right == null) { JOptionPane.showMessageDialog(null, "Please select folders before doing prescan.", "alert", JOptionPane.ERROR_MESSAGE); } else { startJob(PRESCAN); } } else if (o == start_b) { if (opt.left == null || opt.right == null) { JOptionPane.showMessageDialog(null, "Please select folders before doing sync.", "alert", JOptionPane.ERROR_MESSAGE); } else { startJob(DOSYNC); } } else if (o == stop_b) { stop_copy = true; } else if (o == rem_b) { doRemove(); } else if (o == option_b) { doOptions(); } } static String error_message; /** 時間のかかるジョブを別スレッドで実行 */ public synchronized void startJob(int job) { this.job_type = job; Thread thread = new Thread(this); thread.start(); } public synchronized void run() { error_message = "unknown reason"; stop_copy_confirmed = stop_copy = false; stop_b.setEnabled(true); // 中断ボタン switch (job_type) { case PRESCAN: pbar.setValue(0); doPrescan(); if (!stop_copy) start_b.setEnabled(true); break; case DOSYNC: Sync.dbgout("start sync...");//XXX copied_num = 0; pbar.setValue(0); PrintWriter log = null; if (opt.dolog) try { log = new PrintWriter( new BufferedWriter(new FileWriter("log.txt"))); } catch (Exception e) {} doSync(top, opt.dryrun, log); if (log != null) try { log.close(); } catch (Exception e) {} message("Sync done. " + copied_num + " files/folders were copied"); Sync.dbgout("..... done");//XXX break; } stop_b.setEnabled(false); if (stop_copy_confirmed) message("aborted."); if (blinked) dir_b.setBackground(dir_back); } public static File chooseDirectory(Component pane) { JFileChooser chooser = new JFileChooser(); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int return_val = chooser.showOpenDialog(pane); if (return_val == JFileChooser.APPROVE_OPTION) return chooser.getSelectedFile(); return null; } public Sync() { opt = new SyncOptions("sync.ini"); left_b = new JButton(opt.left.getPath()); left_b.addActionListener(this); left_b.setPreferredSize(new Dimension(240, 24)); right_b = new JButton(opt.right.getPath()); right_b.addActionListener(this); right_b.setPreferredSize(new Dimension(240, 24)); left_i = new ImageIcon("Left.gif"); both_i = new ImageIcon("Both.gif"); right_i = new ImageIcon("Right.gif"); dir_b = new JButton(both_i); dir_back = dir_b.getBackground(); dir_b.addActionListener(this); direction = BOTH; JPanel button_pane = new JPanel(); button_pane.setLayout(new BorderLayout()); button_pane.add(left_b, BorderLayout.WEST); button_pane.add(dir_b, BorderLayout.CENTER); button_pane.add(right_b, BorderLayout.EAST); JPanel button_pane2 = new JPanel(); button_pane2.setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2)); prescan_b = new JButton("Prescan"); start_b = new JButton("Start"); start_b.setEnabled(false); stop_b = new JButton("Stop"); stop_b.setEnabled(false); prescan_b.setPreferredSize(new Dimension(80, 24)); start_b.setPreferredSize(new Dimension(80, 24)); stop_b.setPreferredSize(new Dimension(80, 24)); button_pane2.add(prescan_b); prescan_b.addActionListener(this); button_pane2.add(start_b); start_b.addActionListener(this); button_pane2.add(stop_b); stop_b.addActionListener(this); pbar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100); pbar.setValue(0); JPanel tool_pane = new JPanel(); tool_pane.setLayout(new BorderLayout()); tool_pane.add(button_pane, BorderLayout.NORTH); tool_pane.add(button_pane2, BorderLayout.CENTER); tool_pane.add(pbar, BorderLayout.SOUTH); top = new DefaultMutableTreeNode("File List"); top_m = new DefaultTreeModel(top); tree = new JTree(top_m); tree.setLargeModel(true); // 全アイテムが同じ高さ spane = new JScrollPane(tree); spane.setPreferredSize(new Dimension(320, 240)); info_l = new JLabel("information"); info_l.setPreferredSize(new Dimension(280, 20)); rem_b = new JButton("Remove from list"); rem_b.addActionListener(this); option_b = new JButton("Options..."); option_b.addActionListener(this); JPanel bpane = new JPanel(); bpane.setLayout(new BorderLayout()); bpane.add(info_l, BorderLayout.WEST); bpane.add(option_b, BorderLayout.CENTER); bpane.add(rem_b, BorderLayout.EAST); // 最上位パネル this.setLayout(new BorderLayout()); this.add(tool_pane, BorderLayout.NORTH); this.add(spane, BorderLayout.CENTER); this.add(bpane, BorderLayout.SOUTH); } /** prescanした内容(treenodeに保存されている)に従ってコピーを実行 **/ public void doSync(MutableTreeNode dir, boolean dryrun, PrintWriter log) { if (stop_copy_confirmed && dir == null) return; for (int i = 0; i < dir.getChildCount(); i++) try { if (stop_copy_confirmed) return; DefaultMutableTreeNode node = (DefaultMutableTreeNode)dir.getChildAt(i); FilePair fp = (FilePair)node.getUserObject(); if (fp == null) { dbgout("doSync: fp is null" + dir); continue; } if (checkAbort()) return; message(fp.toString()); if (!fp.doAction(dryrun, log)) { int reply = JOptionPane.showConfirmDialog(null, error_message + "\n処理を中断しますか?", "abort?", JOptionPane.YES_NO_OPTION); if (reply == JOptionPane.YES_OPTION) { stop_copy = stop_copy_confirmed = true; return; // abort operation } } copied_num ++; try { pbar.setValue((copied_num * 100) / copy_num); } catch (Exception ex) {} //if (!node.isLeaf()) doSync(node, dryrun, log); } catch (Exception ex) { dbgout("doSync:" + dir + " " + ex); } } private long last_time; private boolean blinked = false; private boolean checkAbort() { try { long t = System.currentTimeMillis(); if (t - last_time > 300) { last_time = t; dir_b.setBackground(blinked ? dir_back : Color.orange); blinked = !blinked; } } catch (Exception e) {} if (!stop_copy) return false; int reply = JOptionPane.showConfirmDialog(null, "処理を中断しますか?", "abort?", JOptionPane.YES_NO_OPTION); if (reply == JOptionPane.YES_OPTION) { stop_copy = stop_copy_confirmed = true; return true; } stop_copy = stop_copy_confirmed = false; return false; } /** obsolete **/ /* public void createCommand(PrintWriter out, MutableTreeNode dir) { if (dir == null) return; for (int i = 0; i < dir.getChildCount(); i++) { DefaultMutableTreeNode node = (DefaultMutableTreeNode)dir.getChildAt(i); FilePair fp = (FilePair)node.getUserObject(); String com = fp.comLine(); if (com != null) out.println(com); if (!node.isLeaf()) createCommand(out, node); } } */ private long copy_size; private int copy_num; private int copied_num; private int scanned_num; private int tocopy_num; public void doPrescan() { message("prescanning..."); top = new DefaultMutableTreeNode( opt.left.getPath() + " =:= " + opt.right.getPath()); top_m = new DefaultTreeModel(top); tree.setModel(top_m); scanned_num = 0; tocopy_num = 0; copy_size = 0; if (opt.left == null) { message("left directory is not defined"); } else if (opt.right == null) { message("right directory is not defined"); } else if (!opt.left.isDirectory()) { message("" + opt.left + " is not a directory"); } else if (!opt.right.isDirectory()) { message("" + opt.right + " is not a directory"); } else { copy_num = prescan(top, opt.left, opt.right, true, true, 30); top_m.reload(); copy_num = tocopy_num; // 2002.1.10 message("" + copy_num + "files (" + (copy_size/1024) + "Kbytes) will be copied"); } } private static File safeNext(Iterator it) { if (it != null && it.hasNext()) return (File)it.next(); return null; } public void quit() { opt.save("sync.ini"); System.exit(0); } /** 転送されるファイルの総量(バイト数)*/ private static Comparator fcomparator; static { fcomparator = new Comparator() { public int compare(Object o1, Object o2) { return fcomp((File)o1, (File)o2); } public boolean equals(Object o2) { return this == o2; } }; } /** file名称で比較するcomparator */ private static int fcomp(File f1, File f2) { if (SyncOptions.case_sensitive) { return f1.getName().compareTo(f2.getName()); } else { String s1 = f1.getName().toLowerCase(); String s2 = f2.getName().toLowerCase(); return s1.compareTo(s2); } // return f1.getName().compareTo(f2.getName()); } /** * コピーすべきファイルを走査する * nest -- ディレクトリの深さ制限 * @return コピーを要するファイルの個数 n */ public int prescan(DefaultMutableTreeNode root, File dir1, File dir2, boolean dir1real, boolean dir2real, int nest) { if (stop_copy_confirmed || nest < 0) return 0; if (dir1real == false && dir2real == false) return 0; /* if ((dir1 != null && dir1real && !dir1.isDirectory()) || (dir2 != null && dir2real && !dir2.isDirectory())) { System.out.println("Sync: not a directory:" + dir1 + " " + dir2); return 0; } */ int num = 0; SortedSet set1 = new TreeSet(fcomparator); SortedSet set2 = new TreeSet(fcomparator); SortedSet fset1 = new TreeSet(fcomparator); SortedSet fset2 = new TreeSet(fcomparator); // ファイルの一覧を取得 if (dir1 != null && dir1real) { File[] list1 = dir1.listFiles(); for (int i = 0; i < list1.length; i++) { if (opt.checkIgnore(list1[i].getName())) continue; if (list1[i].isDirectory()) fset1.add(list1[i]); else set1.add(list1[i]); } } if (dir2 != null && dir2real) { File[] list2 = dir2.listFiles(); for (int i = 0; i < list2.length; i++) { if (opt.checkIgnore(list2[i].getName())) continue; if (list2[i].isDirectory()) fset2.add(list2[i]); else set2.add(list2[i]); } } // ファイルの付き合わせ Iterator it1 = set1.iterator(); Iterator it2 = set2.iterator(); File f1 = safeNext(it1); // safeNext -- 最後はnullを返すnext File f2 = safeNext(it2); FilePair p = null; try { // file名リスト(it1, it2)を順にたどりながら、コピーすべきファイル // のペアとそのコピー方向(FilePairオブジェクト)を記録する。 // 結果はrootに追加される。 while (true) { if (checkAbort()) return num; dbgout("comp: " + f1 + " " + f2);//XXXXX if (f1 == null && f2 == null) { // 両方ともおわり break; } else if (f1 == null || (f2 != null && fcomp(f1, f2) > 0)) { if (opt.force || (direction & LEFT) != 0) { p = new FilePair(dir1, null, dir2,f2, true, FilePair.R2L); root.add(p.makeNode()); num++; tocopy_num++; copy_size += f2.length(); } f2 = safeNext(it2); } else if (f2 == null || (f1 != null && fcomp(f1, f2) < 0)) { if (opt.force || (direction & RIGHT) != 0) { p = new FilePair(dir1, f1, dir2, null, true, FilePair.L2R); root.add(p.makeNode()); num++; tocopy_num++; copy_size += f1.length(); } f1 = safeNext(it1); } else if (f1 != null && f2 != null && fcomp(f1, f2) == 0) { // 同じファイル名 int action = FilePair.EQ; if (Math.abs(f1.lastModified() - f2.lastModified()) <= opt.terror) { action = FilePair.EQ; // same file } else if (f1.lastModified() < f2.lastModified()) { action = FilePair.R2L; // f1 <== f2 } else if (f1.lastModified() > f2.lastModified()) { action = FilePair.L2R; // f1 ==> f2 } if (opt.force || (action == FilePair.R2L && (direction & LEFT) != 0) || (action == FilePair.L2R && (direction & RIGHT) != 0)) { p = new FilePair(dir1, f1, dir2, f2, true, action); root.add(p.makeNode()); num++; tocopy_num++; if (action == FilePair.R2L && (direction & LEFT) != 0) copy_size += f2.length(); if (action == FilePair.L2R && (direction & RIGHT) != 0) copy_size += f1.length(); } f1 = safeNext(it1); f2 = safeNext(it2); } scanned_num++; scanMessage(); } } catch (Exception e) {System.out.println("xx" + e);} Sync.dbgout("Compare Folder");//XXXX // フォルダの処理 it1 = fset1.iterator(); it2 = fset2.iterator(); f1 = safeNext(it1); f2 = safeNext(it2); try { while (true) { if (checkAbort()) return num; Sync.dbgout("Folder" + f1 + "--" + f2);//XXX if (f1 == null && f2 == null) { break; } else if (f1 == null || (f2 != null && fcomp(f1, f2) > 0)) { if (opt.force || (direction & LEFT) != 0) { // <-- // dir1/f2 は存在しない File f1virt = new File(dir1, f2.getName()); p = new FilePair(dir1, null, dir2, f2, false, FilePair.R2L); DefaultMutableTreeNode node = p.makeNode(); root.add(node); int n = prescan(node, f1virt, f2, false, dir2real, nest-1); num += n + 1; tocopy_num++; } f2 = safeNext(it2); } else if (f2 == null || (f1 != null && fcomp(f1, f2) < 0)) { if (opt.force || (direction & RIGHT) != 0) { // --> p = new FilePair(dir1, f1, dir2, null, false, FilePair.L2R); DefaultMutableTreeNode node = p.makeNode(); root.add(node); File f2virt = new File(dir2, f1.getName()); int n = prescan(node, f1, f2virt, dir1real, false, nest-1); num += n + 1; tocopy_num ++; } f1 = safeNext(it1); } else if (f1 != null && f2 != null && fcomp(f1, f2) == 0) { // 同じフォルダ名の場合: 再帰的にprescanを適用 DefaultMutableTreeNode node = new DefaultMutableTreeNode( new FilePair(dir1, f1, dir2, f2, false, FilePair.EQ)); int n = prescan(node, f1, f2, dir1real, dir2real, nest-1); num += n; // フォルダ以下がすべてシンクされていたら、このノードは表示 // する必要がないのでaddしない if (opt.force || n > 0) root.add(node); f1 = safeNext(it1); f2 = safeNext(it2); } scanned_num++; scanMessage(); } } catch (Exception e) {System.out.println("folderscan:" + e);} top_m.reload(); return num; } /** prescanの進捗表示 **/ private void scanMessage() { message("scanning... " + tocopy_num + "/" + scanned_num); checkAbort(); } /** 選択した項目をtreeから削除する */ public void doRemove() { TreePath[] paths = tree.getSelectionPaths(); for (int i = 0; i < paths.length; i++) { Sync.dbgout(paths[i].toString()); DefaultMutableTreeNode node = (DefaultMutableTreeNode)paths[i].getLastPathComponent(); Sync.dbgout("remove node: " + node); node.removeFromParent(); } top_m.reload(); } /** option設定ダイアローグボックスの生成と表示 **/ public void doOptions() { SyncOptions opt_copy = opt.copy(); // 現状をコピー Dialog dialog = new OptionDialog(frame, "Sync Options", opt_copy); dialog.show(); if (opt_copy.valid) { opt = opt_copy; left_b.setText(opt.left.getPath()); right_b.setText(opt.right.getPath()); dbg = opt.debug; opt.save("sync.ini"); needRescan(); } } static boolean dbg = true; public static void dbgout(String s) { if (dbg) System.out.println("|"+s); } public static void main(String[] args) { String l_f = UIManager.getSystemLookAndFeelClassName(); if (args != null) { for (int i = 0; i < args.length; i++) { if (args[i].equals("-cross")) { l_f = UIManager.getCrossPlatformLookAndFeelClassName(); } else if (args[i].equals("-debug")) { Sync.dbg = true; } } } try {UIManager.setLookAndFeel(l_f);} catch (Exception e) {} //Create the top-level container and add contents to it. JFrame frame = new JFrame("FileSync v020111"); Sync app = new Sync(); app.frame = frame; frame.getContentPane().add(app, BorderLayout.CENTER); //Finish setting up the frame, and show it. frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); frame.pack(); frame.setVisible(true); } }