/** * Settings.java * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org * * This program 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. * * This program 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 this program. If not, see . */ package org.expeditee.settings; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.expeditee.gui.AttributeValuePair; import org.expeditee.gui.Frame; import org.expeditee.gui.FrameCreator; import org.expeditee.gui.MessageBay; import org.expeditee.items.Text; import org.expeditee.items.widgets.Password; import org.expeditee.reflection.PackageLoader; import org.expeditee.setting.Setting; import org.expeditee.setting.VariableSetting; public abstract class Settings { public static final String SETTINGS_PACKAGE_PARENT = "org.expeditee."; public static final String SETTINGS_PACKAGE = SETTINGS_PACKAGE_PARENT + "settings."; private static final class PageDescriptor { private final HashMap settings = new HashMap(); private final List orderedEntries = new LinkedList(); private final List settingsList = new LinkedList(); private final Method onParsed; private PageDescriptor(Class clazz) { // populate map of settings for(Field f : clazz.getFields()) { // Only allow classes that inherit from Setting if(!Setting.class.isAssignableFrom(f.getType())) continue; try { Setting s = (Setting) f.get(null); settings.put(f.getName().toLowerCase(), s); if (s instanceof VariableSetting) settingsList.add((VariableSetting) s); orderedEntries.add(f.getName()); } catch (Exception e) { e.printStackTrace(); } } Method m = null; try { m = clazz.getMethod("onParsed", Text.class); } catch(Exception e) { // System.err.println(clazz.getName() + " has no onParsed(Text t) callback"); } this.onParsed = m; } } private static HashMap _pages = new HashMap(); private static boolean _init = false; public static void Init() { if(_init) return; _init = true; try { for(Class clazz : PackageLoader.getClassesNew(SETTINGS_PACKAGE)) { // Ignore this class since it's the controller if(clazz.equals(Settings.class)) continue; String settingsPageName = clazz.getPackage().getName().toLowerCase().substring(SETTINGS_PACKAGE_PARENT.length()); // System.out.println(settingsPage + " : " + clazz.getName()); _pages.put(settingsPageName, new PageDescriptor(clazz)); } } catch (Exception e) { e.printStackTrace(); } } /** Parses the settings tree, then resets any settings that were not set. */ public static void parseSettings(Text text) { List set = parseSettings(text, ""); List toDefault = new LinkedList(); for(PageDescriptor pd : _pages.values()) toDefault.addAll(pd.settingsList); toDefault.removeAll(set); for(VariableSetting s : toDefault) { try { s.reset(); } catch (Exception e) { e.printStackTrace(); } } } public static void resetAllSettings() { for (PageDescriptor pd : _pages.values()) { for (VariableSetting s : pd.settingsList) { s.reset(); } } } /** * Sets all the simple settings. * * @param text * @param prefix * * @return List of VariableSettings that were changed */ private static List parseSettings(Text text, String prefix) { List set = new LinkedList(); Frame child = text.getChild(); if(child == null) return set; String settingsPage = prefix + text.getText().trim().toLowerCase().replaceAll("^@", ""); PageDescriptor pd = _pages.get(settingsPage); if(pd == null) return set; try { // set the fields List items = child.getBodyTextItems(false); List annotations = new LinkedList(child.getAnnotationItems()); List seen = new LinkedList(); // to avoid getting stuck in a loop seen.add(child); // find all the frames for this settings page while(!annotations.isEmpty()) { Text annotation = annotations.remove(0); Frame next = annotation.getChild(); if(next != null && !seen.contains(next)) { items.addAll(next.getBodyTextItems(false)); annotations.addAll(next.getAnnotationItems()); seen.add(next); } } // parse all the settings items on this page for(Text t : items) { AttributeValuePair avp = new AttributeValuePair(t.getText(), false); try { String settingName = avp.getAttributeOrValue().trim().toLowerCase(); // System.out.println(avp.getAttributeOrValue().trim().toLowerCase().replaceAll("^@", "")); Setting s = pd.settings.get(settingName);//.replaceAll("^@", "")); if(s == null) { if(settingName.startsWith("//") || settingName.startsWith("@")) { continue; } // System.out.println("Couldn't find setting \"" + settingName + "\""); List validPages = new LinkedList(); // if the setting isn't found on the current page, check all child pages for(String k : _pages.keySet()) { if(k.startsWith(settingsPage) && k.length() > settingsPage.length()) { // System.out.println(k + " is a child of " + settingsPage); PageDescriptor cpd = _pages.get(k); Setting tmp = cpd.settings.get(settingName); if(tmp != null) { // System.out.println("Found setting in subpage: " + k); s = tmp; validPages.add(k); } } } if(s == null) continue; if(validPages.size() > 1) { StringBuffer warnMessage = new StringBuffer("Found multiple matching settings in the following settings subpages: "); String lastPage = ""; for(String page : validPages) { warnMessage.append("\"" + page + "\","); lastPage = page; } warnMessage.deleteCharAt(warnMessage.length() - 1); warnMessage.append(" - choosing " + lastPage); MessageBay.warningMessage(warnMessage.toString()); } } if(s.setSetting(t) && s instanceof VariableSetting) set.add((VariableSetting) s); } catch (Exception e) { e.printStackTrace(); } } // call the onParsed method if one exists if(pd.onParsed != null) { pd.onParsed.invoke(null, text); } } catch (Exception e) { e.printStackTrace(); return set; } // if the page was a settings page, check if it has any subpages for(Text t : child.getTextItems()) { set.addAll(parseSettings(t, settingsPage + ".")); } return set; } public static void generateSettingsTree(Text link) { generateSettingsTree("settings", link); } private static void generateSettingsTree(String page, Text text) { Frame parent = text.getParentOrCurrentFrame(); FrameCreator frames = new FrameCreator(parent.getFramesetName(), parent.getPath(), page, FrameCreator.ExistingFramesetOptions.AppendSegregatedFrames, false, null); text.setLink(frames.getName()); // Add subpages of the current page by recursing for (String k: _pages.keySet()) { if (k.startsWith(page.toLowerCase()) && !k.equals(page)) { String name = k.substring(page.length() + 1); if (name.indexOf('.') != -1) { continue; } System.out.println("@Settings: Generating " + name); generateSettingsTree(k, frames.addText(name.substring(0, 1).toUpperCase() + name.substring(1), null, null, null, false)); } } // Start building current page frames.setLastY(frames.getLastY() + 20); // Add Settings of the current page PageDescriptor pd = _pages.get(page); if (pd == null) { frames.save(); return; } //Iterator keys = pd.orderedEntries.stream().map(t -> t.toLowerCase()).iterator(); Iterator keys = pd.orderedEntries.iterator(); while(keys.hasNext()) { String key = keys.next(); Setting setting = pd.settings.get(key.toLowerCase()); if (setting == null) { continue; } // Keep track of were we are placing items on Frames. int x = 0, y = 0; if (key.toLowerCase().equals("pass")) { // Special case for Password widgets Text passwordWidgetText = frames.addText("iw: org.expeditee.items.widgets.Passwsord", null, null, null, false); Password pw = new Password(passwordWidgetText, null); pw.setPassword(""); frames.getCurrentFrame().removeItem(passwordWidgetText); frames.getCurrentFrame().addAllItems(pw.getItems()); x = passwordWidgetText.getX() + passwordWidgetText.getBoundsWidth(); y = passwordWidgetText.getY(); } else { // Determine the content for a setting label. //String name = key.substring(0, 1).toUpperCase() + key.substring(1); //String name = key; // Construct and add text representation for setting. // If a setting has no initialised value then it is not included. Text settingRepresentation = setting.generateRepresentation(key, frames.getCurrentFrame().getFramesetName()).copy(); if (settingRepresentation.getBounds() == null) { continue; } settingRepresentation.setID(frames.getCurrentFrame().getNextItemID()); frames.addItem(settingRepresentation, false); x = settingRepresentation.getX() + settingRepresentation.getBoundsWidth(); y = settingRepresentation.getY(); } x = Math.max(250, x + 20); // Add tooltip for setting Text tooltip = frames.getCurrentFrame().addText(x, y, "// " + setting.getTooltip(), null); tooltip.rebuild(true); if (tooltip.getY() + tooltip.getBoundsHeight() > frames.getLastY()) { frames.setLastY(tooltip.getY() + tooltip.getBoundsHeight()); } } frames.save(); } }