/**
* 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();
}
}