package net.sourceforge.fidocadj.globals; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.prefs.Preferences; import java.util.Locale; import net.sourceforge.fidocadj.primitives.GraphicPrimitive; import net.sourceforge.fidocadj.primitives.MacroDesc; import net.sourceforge.fidocadj.undo.*; import net.sourceforge.fidocadj.FidoMain; /** Class to handle library files and databases. <pre> This file is part of FidoCadJ. FidoCadJ is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. FidoCadJ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with FidoCadJ. If not, @see <a href=http://www.gnu.org/licenses/>http://www.gnu.org/licenses/</a>. Copyright 2012-2014 by phylum2, Davide Bucci </pre> @author phylum2, Davide Bucci */ public final class LibUtils { /** Private constructor, for Utility class pattern */ private LibUtils () { // nothing } /** Extract all the macros belonging to a given library. @param m the macro list. @param libfile the file name of the wanted library. @return the library. */ public static Map<String,MacroDesc> getLibrary(Map<String,MacroDesc> m, String libfile) { Map<String,MacroDesc> mm = new TreeMap<String,MacroDesc>(); MacroDesc md; for (Entry<String, MacroDesc> e : m.entrySet()) { md = e.getValue(); // The most reliable way to discriminate the macros is to watch // at the prefix in the key, i.e. everything which comes // before the dot in the complete key. int dotPos = md.key.lastIndexOf("."); // If no dot is found, this is by definition the original FidoCAD // standard library (immutable). if(dotPos<0) continue; String lib = md.key.substring(0,dotPos).trim(); if (lib.equalsIgnoreCase(libfile)) { mm.put(e.getKey(), md); } } return mm; } /** Prepare an header and collect text for creating a complete library. @param m the macro map associated to the library @param name the name of the library @return the library description in FidoCadJ code. */ public static String prepareText(Map<String,MacroDesc> m, String name) { StringBuffer sb = new StringBuffer(); String prev = null; int u; MacroDesc md; // Header sb.append("[FIDOLIB " + name + "]\n"); for (Entry<String,MacroDesc> e : m.entrySet()) { md = e.getValue(); // Category (check if it is changed) if (prev == null || !prev.equalsIgnoreCase(md.category.trim())) { sb.append("{"+md.category+"}\n"); prev = md.category.toLowerCase(new Locale("en")).trim(); } sb.append("["); // When the macros are written in the library, they contain only // the last part of the key, since the first part (before the .) // is always the file name. sb.append(md.key.substring( md.key.lastIndexOf(".")+1).toUpperCase().trim()); sb.append(" "); sb.append(md.name.trim()); sb.append("]"); u = md.description.codePointAt(0) == '\n'?1:0; sb.append("\n"); sb.append(md.description.substring(u)); sb.append("\n"); } return sb.toString(); } /** Save to a file a string respecting the global encoding settings. @param file the file name. @param text the string to be written in the file. @throws FileNotFoundException if the file can not be accessed. */ public static void saveToFile(String file, String text) throws FileNotFoundException { PrintWriter pw; try { pw = new PrintWriter(file, Globals.encoding); pw.print(text); pw.flush(); pw.close(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } /** Save a library in a file. @param m the map containing the library. @param file the file name. @param libname the name of the library. @param prefix the prefix to be used for the keys. */ public static void save(Map<String,MacroDesc> m, String file, String libname, String prefix) { try { LibUtils.saveToFile(file + ".fcl", LibUtils.prepareText( LibUtils.getLibrary(m, prefix), libname)); } catch (FileNotFoundException e) { e.printStackTrace(); } } /** Get the directory where the libraries files are to be read. @return the path to the directory. @throws FileNotFoundException if the directory can not be accessed. */ public static String getLibDir() throws FileNotFoundException { Preferences prefs = Preferences.userNodeForPackage(FidoMain.class); String s = prefs.get("DIR_LIBS", ""); if (s == null || s.length()==0) { throw new FileNotFoundException(); } if (!s.endsWith(System.getProperty("file.separator"))) s+=System.getProperty("file.separator"); return s; } /** Returns full path to lib file. @param lib Library name. @return the full path as a String. @throws FileNotFoundException if the file can not be accessed. */ public static String getLibPath(String lib) throws FileNotFoundException { return getLibDir()+lib.trim(); } /** Eliminates a library. @param s Name of the library to eliminate. @throws FileNotFoundException if the file can not be accessed. @throws IOException if a generic IO error occurs. */ public static void deleteLib(String s) throws FileNotFoundException, IOException { File f = new File(getLibDir()+s+".fcl"); if(!f.delete()) throw new IOException("Can not delete library."); } /** Get all the library in the current library directory. @return a list containing all the library files. @throws FileNotFoundException if the files can not be accessed. */ public static List<File> getLibs() throws FileNotFoundException { File lst = new File(LibUtils.getLibDir()); List<File> l = new ArrayList<File>(); if (!lst.exists()) return null; File[] list=lst.listFiles(); if(list==null) return l; for (File f : list) { if (f.getName().toLowerCase().endsWith(".fcl")) l.add(f); } return l; } /** Determine whether a library is standard or not. @param tlib the name (better prefix?) of the library @return true if the specified library is standard */ public static boolean isStdLib(MacroDesc tlib) { String szlib=tlib.library; String szfn=tlib.filename; if(szlib==null) return false; boolean isStandard=false; int dotpos=-1; boolean extensions=true; // A first way to determine if a macro is standard is to see if its // name does not contains a dot (original FidoCAD standard library) if ((dotpos=tlib.key.indexOf("."))<0) { isStandard = true; } else { // If the name contains a dot, we might check whether we have // one of the new FidoCadJ standard libraries: // pcb, ihram, elettrotecnica, ey_libraries. // Obtain the library name String library=tlib.key.substring(0,dotpos); // Check it if(extensions && "pcb".equals(library)) { isStandard = true; } else if (extensions && "ihram".equals(library)) { isStandard = true; } else if (extensions && "elettrotecnica".equals(library)) { isStandard = true; } else if (extensions && "ey_libraries".equals(library)) { isStandard = true; } } return isStandard; } /** Rename a group inside a library. @param libref the map containing the library. @param tlib the name of the library. @param tgrp the name of the group to be renamed. @param newname the new name of the group. @throws FileNotFoundException if the file can not be accessed. */ public static void renameGroup(Map<String, MacroDesc> libref, String tlib, String tgrp, String newname) throws FileNotFoundException { // TODO: what if a group is not present? String prefix=""; for (MacroDesc md : libref.values()) { if (md.category.equalsIgnoreCase(tgrp) && md.library.trim().equalsIgnoreCase( tlib.trim())) { md.category = newname; prefix = md.filename; } } if ("".equals(prefix)) return; save(libref, getLibPath(tlib), tlib.trim(), prefix); } /** Check whether a key is used in a given library or it is available. The code also check for the presence of ']', a forbidden char since it would mess up the FidoCadJ file. Also check for strange characters. @param libref the map containing the library. @param tlib the name of the library. @param key the key to be checked. @return false if the key is available, true if it is used. */ public static boolean checkKey(Map<String, MacroDesc> libref, String tlib,String key) { for (MacroDesc md : libref.values()) { if (md.library.equalsIgnoreCase(tlib) && md.key.equalsIgnoreCase(key.trim())) return true; } return key.contains("]"); } /** Check if a library name is acceptable. Since the library name is used also as a file name, it must not contain characters which would be in conflict with the rules of file names in the various operating systems. @param library the library name to be checked. @return true if something strange is found. */ public static boolean checkLibrary(String library) { if (library == null) return false; return library.contains("[")||library.contains(".")|| library.contains("/")||library.contains("\\")|| library.contains("~")||library.contains("&")|| library.contains(",")||library.contains(";")|| library.contains("]")||library.contains("\""); } /** Delete a group inside a library. @param m the map containing the library. @param tlib the library name. @param tgrp the group to be deleted. @throws FileNotFoundException if the file can not be accessed. */ public static void deleteGroup(Map<String, MacroDesc> m,String tlib, String tgrp) throws FileNotFoundException { // TODO: what if a group is not found? Map<String, MacroDesc> mm = new TreeMap<String, MacroDesc>(); mm.putAll(m); String prefix=""; for (Entry<String, MacroDesc> smd : mm.entrySet()) { MacroDesc md = smd.getValue(); if (md.library.trim().equalsIgnoreCase(tlib) && md.category.equalsIgnoreCase(tgrp)) { m.remove(md.key); prefix = md.filename; } } if("".equals(prefix)) return; save(m, getLibPath(tlib), tlib, prefix); } /** Obtain a list containing all the groups in a given library. @param m the map containing all the libraries. @param prefix the filename of the wanted library. @return the list of groups. */ public static List<String> enumGroups(Map<String,MacroDesc> m, String prefix) { List<String> lst = new LinkedList<String>(); for (MacroDesc md : m.values()) { if (!lst.contains(md.category) && prefix.trim().equalsIgnoreCase(md.filename.trim())) { lst.add(md.category); } } return lst; } /** Obtain the full name of a library, from the prefix. @param m the map containing all the libraries. @param prefix the filename of the wanted library. @return the library name. */ public static String getLibName(Map<String,MacroDesc> m, String prefix) { List lst = new LinkedList(); for (MacroDesc md : m.values()) { if (!lst.contains(md.category) && prefix.trim().equalsIgnoreCase(md.filename.trim())) { return md.library; } } return null; } /** Here we save the state of the library for the undo operation. We create a temporary directory and we copy all the contents of the current library directory inside it. The temporary directory name is then saved in the undo system. @param ua the undo controller. @throws IOException if the files or directories needed for the undo can not be accessed. */ public static void saveLibraryState(UndoActorListener ua) throws IOException { try { // This is an hack: at first, we create a temporary file. We store // its name and we use it to create a temporary directory. File tempDir = File.createTempFile("fidocadj_", ""); if(!tempDir.delete()) throw new IOException( "saveLibraryState: Can not delete temp file."); if(!tempDir.mkdir()) throw new IOException( "saveLibraryState: Can not create temp directory."); String s=LibUtils.getLibDir(); String d=tempDir.getAbsolutePath(); // We copy all the contents of the current library directory in the // temporary directory. File sourceDir = new File(s); File destinationDir = new File(d); FileUtils.copyDirectoryNonRecursive(sourceDir, destinationDir, "fcl"); // We store the directory name in the stack structure of the // undo system. if(ua != null) ua.saveUndoLibrary(d); } catch (IOException e) { System.out.println("Cannot save the library status."); } } }