/*
* Copyright 2008 (C) Tom Parker <thpr@users.sourceforge.net>
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package plugin.lsttokens.testsupport;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import pcgen.base.lang.StringUtil;
import pcgen.cdom.base.CDOMObject;
import pcgen.persistence.PersistenceLayerException;
import pcgen.persistence.lst.CampaignSourceEntry;
import pcgen.persistence.lst.LstFileLoader;
import pcgen.rules.context.LoadContext;
import pcgen.rules.persistence.CDOMLoader;
import pcgen.util.Logging;
import pcgen.system.LanguageBundle;
public class CDOMTokenLoader<T extends CDOMObject> implements CDOMLoader<T>
{
private final List<ModEntry> copyList = new ArrayList<ModEntry>();
private final Class<T> targetClass;
public CDOMTokenLoader(Class<T> cl)
{
targetClass = cl;
}
@Override
public boolean parseLine(LoadContext context, T obj, String val, URI source)
throws PersistenceLayerException
{
if (val == null)
{
return true;
}
boolean returnValue = true;
StringTokenizer st = new StringTokenizer(val, "\t");
while (st.hasMoreTokens())
{
String token = st.nextToken().trim();
int colonLoc = token.indexOf(':');
if (colonLoc == -1)
{
Logging.errorPrint("Invalid Token - does not contain a colon: "
+ token);
returnValue &= false;
continue;
}
else if (colonLoc == 0)
{
Logging.errorPrint("Invalid Token - starts with a colon: "
+ token);
returnValue &= false;
}
String key = token.substring(0, colonLoc);
String value = (colonLoc == token.length() - 1) ? null : token
.substring(colonLoc + 1);
if (context.processToken(obj, key.intern(), value.intern()))
{
context.commit();
}
else
{
context.rollback();
Logging.replayParsedMessages();
returnValue &= false;
}
Logging.clearParseMessages();
}
return returnValue;
}
@Override
public void loadLstFiles(LoadContext context,
Collection<CampaignSourceEntry> sources)
{
copyList.clear();
// Track which sources have been loaded already
Set<CampaignSourceEntry> loadedSources = new HashSet<CampaignSourceEntry>();
// Load the files themselves as thoroughly as possible
for (CampaignSourceEntry sourceEntry : sources)
{
// Check if the CSE has already been loaded before loading it
if (!loadedSources.contains(sourceEntry))
{
loadLstFile(context, sourceEntry);
loadedSources.add(sourceEntry);
}
}
processCopies(context);
copyList.clear();
// Now handle .MOD items
// TODO processMods();
// Finally, forget the .FORGET items
// TODO processForgets();
}
private void loadLstFile(LoadContext context,
CampaignSourceEntry sourceEntry)
{
URI uri = sourceEntry.getURI();
context.setSourceURI(uri);
StringBuilder dataBuffer;
try
{
dataBuffer = LstFileLoader.readFromURI(uri);
}
catch (PersistenceLayerException ple)
{
String message = LanguageBundle.getFormattedString(
"Errors.LstFileLoader.LoadError", //$NON-NLS-1$
uri, ple.getMessage());
Logging.errorPrint(message);
return;
}
final String aString = dataBuffer.toString();
String[] fileLines = aString.split(LstFileLoader.LINE_SEPARATOR_REGEXP);
for (int i = 0; i < fileLines.length; i++)
{
parseFullLine(context, i, fileLines[i], sourceEntry);
}
}
public void parseFullLine(LoadContext context, int i, String line,
CampaignSourceEntry sourceEntry)
{
if ((line.length() == 0)
|| (line.charAt(0) == LstFileLoader.LINE_COMMENT_CHAR))
{
return;
}
URI uri = sourceEntry.getURI();
int sepLoc = line.indexOf('\t');
String firstToken;
String restOfLine;
if (sepLoc == -1)
{
firstToken = line;
restOfLine = null;
}
else
{
firstToken = line.substring(0, sepLoc);
restOfLine = line.substring(sepLoc + 1);
}
// check for copies, mods, and forgets
// TODO - Figure out why we need to check SOURCE in this file
if (line.startsWith("SOURCE")) //$NON-NLS-1$
{
// TODO sourceMap = SourceLoader.parseLine(line,
// sourceEntry.getURI());
}
else if (firstToken.indexOf(".COPY") > 0)
{
copyList.add(new ModEntry(sourceEntry, line, i + 1));
}
else if (firstToken.indexOf(".MOD") > 0)
{
// TODO modEntryList.add(new ModEntry(sourceEntry, line, i +
// 1));
}
else if (firstToken.indexOf(".FORGET") > 0)
{
// TODO forgetLineList.add(line);
}
else
{
try
{
parseLine(context, getCDOMObject(context, firstToken.intern()),
restOfLine, uri);
}
catch (PersistenceLayerException ple)
{
String message = LanguageBundle.getFormattedString(
"Errors.LstFileLoader.ParseError", //$NON-NLS-1$
uri, i + 1, ple.getMessage());
Logging.errorPrint(message);
Logging.debugPrint("Parse error:", ple); //$NON-NLS-1$
}
catch (Throwable t)
{
String message = LanguageBundle.getFormattedString(
"Errors.LstFileLoader.ParseError", //$NON-NLS-1$
uri, i + 1, t.getMessage());
Logging.errorPrint(message);
Logging.errorPrint(LanguageBundle
.getString("Errors.LstFileLoader.Ignoring")
+ "\n" + t);
t.printStackTrace();
}
}
}
protected T getCDOMObject(LoadContext context, String firstToken)
{
return context.getReferenceContext().constructCDOMObject(targetClass, firstToken);
}
private void processCopies(LoadContext context)
{
for (ModEntry me : copyList)
{
String line = me.lstLine;
int sepLoc = line.indexOf('\t');
String firstToken;
String restOfLine;
if (sepLoc == -1)
{
firstToken = line;
restOfLine = null;
}
else
{
firstToken = line.substring(0, sepLoc);
restOfLine = line.substring(sepLoc + 1);
}
int copyLoc = firstToken.indexOf(".COPY=");
String sourceName = firstToken.substring(0, copyLoc);
String copyName = firstToken.substring(copyLoc + 6);
T sourceObj = context.getReferenceContext().silentlyGetConstructedCDOMObject(
targetClass, sourceName);
if (sourceObj == null)
{
Logging.errorPrint("Attempt to copy " + targetClass.getName()
+ " " + sourceName + " but it was never built");
continue;
}
T copy = context.cloneConstructedCDOMObject(sourceObj, copyName.intern());
try
{
parseLine(context, copy, restOfLine, me.source.getURI());
}
catch (PersistenceLayerException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static class ModEntry
{
public final CampaignSourceEntry source;
public final String lstLine;
public final int lineNumber;
public ModEntry(CampaignSourceEntry sourceEntry, String line, int i)
{
source = sourceEntry;
lstLine = line;
lineNumber = i;
}
}
@Override
public void unloadLstFiles(LoadContext lc,
Collection<CampaignSourceEntry> files)
{
for (CampaignSourceEntry cse : files)
{
lc.setExtractURI(cse.getURI());
URI writeURI = cse.getWriteURI();
File f = new File(writeURI);
ensureCreated(f.getParentFile());
try
{
PrintWriter pw = new PrintWriter(f);
Collection<T> objects = lc.getReferenceContext()
.getConstructedCDOMObjects(targetClass);
Set<String> set = new TreeSet<String>();
for (T obj : objects)
{
String s = unparseObject(lc, cse, obj);
if (s != null)
{
set.add(s);
}
}
for (String s : set)
{
pw.println(s);
}
pw.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public String unparseObject(LoadContext lc, CampaignSourceEntry cse, T obj)
{
String unparse = StringUtil.join(lc.unparse(obj), "\t");
/*
* TODO This isn't good enough - you can .MOD in the
* original file, and that needs to be remembered
*/
if (cse.getURI().equals(obj.getSourceURI()))
{
return obj.getDisplayName() + '\t' + unparse;
}
else if (unparse.length() != 0)
{
return obj.getKeyName() + ".MOD\t" + unparse;
}
return null;
}
private boolean ensureCreated(File rec)
{
if (!rec.exists())
{
if (!ensureCreated(rec.getParentFile()))
{
return false;
}
return rec.mkdir();
}
return true;
}
}