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.");
}
}
}