/*
* Copyright (c) 2009 Tom Parker <thpr@users.sourceforge.net>
*
* This program 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 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 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package pcgen.gui2.converter;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Observable;
import java.util.Set;
import pcgen.base.util.DoubleKeyMapToList;
import pcgen.cdom.base.CDOMObject;
import pcgen.cdom.enumeration.ListKey;
import pcgen.core.Ability;
import pcgen.core.ArmorProf;
import pcgen.core.Campaign;
import pcgen.core.Deity;
import pcgen.core.Domain;
import pcgen.core.EquipmentModifier;
import pcgen.core.Globals;
import pcgen.core.Language;
import pcgen.core.PCAlignment;
import pcgen.core.PCCheck;
import pcgen.core.PCStat;
import pcgen.core.PCTemplate;
import pcgen.core.Race;
import pcgen.core.ShieldProf;
import pcgen.core.SizeAdjustment;
import pcgen.core.Skill;
import pcgen.core.WeaponProf;
import pcgen.core.character.CompanionMod;
import pcgen.core.spell.Spell;
import pcgen.gui2.converter.loader.AbilityLoader;
import pcgen.gui2.converter.loader.BasicLoader;
import pcgen.gui2.converter.loader.ClassLoader;
import pcgen.gui2.converter.loader.CopyLoader;
import pcgen.gui2.converter.loader.EquipmentLoader;
import pcgen.gui2.converter.loader.SelfCopyLoader;
import pcgen.persistence.PersistenceLayerException;
import pcgen.persistence.SourceFileLoader;
import pcgen.persistence.lst.AbilityCategoryLoader;
import pcgen.persistence.lst.CampaignSourceEntry;
import pcgen.persistence.lst.GenericLoader;
import pcgen.persistence.lst.LstFileLoader;
import pcgen.rules.context.EditorLoadContext;
import pcgen.rules.persistence.CDOMControlLoader;
import pcgen.system.LanguageBundle;
import pcgen.util.Logging;
public class LSTConverter extends Observable
{
private final AbilityCategoryLoader catLoader = new AbilityCategoryLoader();
private final GenericLoader<SizeAdjustment> sizeLoader = new GenericLoader<>(SizeAdjustment.class);
private final GenericLoader<PCCheck> savesLoader = new GenericLoader<>(PCCheck.class);
private final GenericLoader<PCAlignment> alignmentLoader = new GenericLoader<>(PCAlignment.class);
private final GenericLoader<PCStat> statLoader = new GenericLoader<>(PCStat.class);
private final CDOMControlLoader dataControlLoader = new CDOMControlLoader();
private final EditorLoadContext context;
private List<Loader> loaders;
private Set<URI> written = new HashSet<>();
private final String outDir;
private final File rootDir;
private final DoubleKeyMapToList<Loader, URI, CDOMObject> injected = new DoubleKeyMapToList<>();
private final ConversionDecider decider;
private Writer changeLogWriter;
public LSTConverter(EditorLoadContext lc, File root, String outputDir,
ConversionDecider cd, Writer changeLogWriter)
{
context = lc;
rootDir = root;
outDir = outputDir;
decider = cd;
this.changeLogWriter = changeLogWriter;
loaders = setupLoaders(context, changeLogWriter);
}
/**
* Return the number of files referred to by the campaign
* @param campaign The campaign to be tallied.
* @return The number of lst files used.
*/
public int getNumFilesInCampaign(Campaign campaign)
{
int numFiles = 0;
for (final Loader loader : loaders)
{
List<CampaignSourceEntry> files = loader.getFiles(campaign);
numFiles += files.size();
}
return numFiles;
}
/**
* Initialise the list of campaigns. This will load the ability
* categories in advance of the conversion.
* @param campaigns The campaigns or sources to be converted.
*/
public void initCampaigns(List<Campaign> campaigns)
{
List<CampaignSourceEntry> dataDefFileList = new ArrayList<>();
for (Campaign campaign : campaigns)
{
// load ability categories first as they used to only be at the game
// mode
try
{
catLoader.loadLstFiles(context, campaign
.getSafeListFor(ListKey.FILE_ABILITY_CATEGORY));
sizeLoader.loadLstFiles(context,
campaign.getSafeListFor(ListKey.FILE_SIZE));
statLoader.loadLstFiles(context,
campaign.getSafeListFor(ListKey.FILE_STAT));
savesLoader.loadLstFiles(context,
campaign.getSafeListFor(ListKey.FILE_SAVE));
alignmentLoader.loadLstFiles(context,
campaign.getSafeListFor(ListKey.FILE_ALIGNMENT));
alignmentLoader.loadLstFiles(Globals.getContext(),
campaign.getSafeListFor(ListKey.FILE_ALIGNMENT));
}
catch (PersistenceLayerException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
dataDefFileList.addAll(campaign
.getSafeListFor(ListKey.FILE_DATACTRL));
}
// Load using the new LstFileLoaders
try
{
SourceFileLoader.addDefaultDataControlIfNeeded(dataDefFileList);
dataControlLoader.loadLstFiles(context, dataDefFileList);
SourceFileLoader.processFactDefinitions(context);
}
catch (PersistenceLayerException e)
{
// TODO Auto-generated catch block
Logging.errorPrint("LSTConverter.initCampaigns failed", e);
}
}
public void processCampaign(Campaign campaign)
{
startItem(campaign);
}
private void startItem(final Campaign campaign)
{
for (final Loader loader : loaders)
{
List<CampaignSourceEntry> files = loader.getFiles(campaign);
for (final CampaignSourceEntry cse : files)
{
final URI uri = cse.getURI();
setChanged();
notifyObservers(uri);
if (!"file".equalsIgnoreCase(uri.getScheme()))
{
Logging.log(Logging.WARNING, "Skipping campaign " + uri
+ " from " + campaign.getSourceURI()
+ " as it is not a local file.");
continue;
}
File in = new File(uri);
// Use canonical name to stop reruns for the same file referred to using ..
URI canonicalUri;
try
{
canonicalUri = in.getCanonicalFile().toURI();
}
catch (IOException e1)
{
Logging.log(
Logging.WARNING,
"Skipping campaign " + uri + " from "
+ campaign.getSourceURI()
+ " as it could not be made canonical. "
+ e1.getMessage());
continue;
}
if (written.contains(canonicalUri))
{
continue;
}
written.add(canonicalUri);
File base = findSubRoot(rootDir, in);
if (base == null)
{
Logging.log(Logging.WARNING, "Skipping campaign " + uri
+ " from " + campaign.getSourceURI()
+ " as it is not in the selected source directory.");
continue;
}
String relative = in.toString().substring(
base.toString().length() + 1);
if (!in.exists())
{
Logging.log(Logging.WARNING, "Skipping campaign " + uri
+ " from " + campaign.getSourceURI()
+ " as it does not exist. Campaign is "
+ cse.getCampaign().getSourceURI());
continue;
}
File outFile = new File(outDir, File.separator + relative);
if (outFile.exists())
{
Logging.log(Logging.WARNING, "Won't overwrite: " + outFile);
continue;
}
ensureParents(outFile.getParentFile());
try
{
changeLogWriter.append("\nProcessing " + in + "\n");
String result = load(uri, loader);
if (result != null)
{
Writer out =
new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(outFile), "UTF-8"));
out.write(result);
out.close();
}
}
catch (PersistenceLayerException | IOException | InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private List<Loader> setupLoaders(EditorLoadContext context, Writer changeLogWriter)
{
List<Loader> loaderList = new ArrayList<>();
loaderList.add(new BasicLoader<>(context, WeaponProf.class,
ListKey.FILE_WEAPON_PROF, changeLogWriter));
loaderList.add(new BasicLoader<>(context, ArmorProf.class,
ListKey.FILE_ARMOR_PROF, changeLogWriter));
loaderList.add(new BasicLoader<>(context, ShieldProf.class,
ListKey.FILE_SHIELD_PROF, changeLogWriter));
loaderList.add(new BasicLoader<>(context, Skill.class,
ListKey.FILE_SKILL, changeLogWriter));
loaderList.add(new BasicLoader<>(context, Language.class,
ListKey.FILE_LANGUAGE, changeLogWriter));
loaderList.add(new BasicLoader<>(context, Ability.class,
ListKey.FILE_FEAT, changeLogWriter));
loaderList.add(new AbilityLoader(context, Ability.class,
ListKey.FILE_ABILITY, changeLogWriter));
loaderList.add(new BasicLoader<>(context, Race.class,
ListKey.FILE_RACE, changeLogWriter));
loaderList.add(new BasicLoader<>(context, Domain.class,
ListKey.FILE_DOMAIN, changeLogWriter));
loaderList.add(new BasicLoader<>(context, Spell.class,
ListKey.FILE_SPELL, changeLogWriter));
loaderList.add(new BasicLoader<>(context, Deity.class,
ListKey.FILE_DEITY, changeLogWriter));
loaderList.add(new BasicLoader<>(context, PCTemplate.class,
ListKey.FILE_TEMPLATE, changeLogWriter));
loaderList.add(new EquipmentLoader(context, ListKey.FILE_EQUIP,
changeLogWriter));
loaderList.add(new BasicLoader<>(context,
EquipmentModifier.class, ListKey.FILE_EQUIP_MOD, changeLogWriter));
loaderList.add(new BasicLoader<>(context, CompanionMod.class,
ListKey.FILE_COMPANION_MOD, changeLogWriter));
loaderList.add(new ClassLoader(context, changeLogWriter));
loaderList.add(new CopyLoader(ListKey.FILE_ABILITY_CATEGORY));
loaderList.add(new CopyLoader(ListKey.LICENSE_FILE));
loaderList.add(new CopyLoader(ListKey.FILE_KIT));
loaderList.add(new CopyLoader(ListKey.FILE_BIO_SET));
loaderList.add(new CopyLoader(ListKey.FILE_DATACTRL));
loaderList.add(new CopyLoader(ListKey.FILE_STAT));
loaderList.add(new CopyLoader(ListKey.FILE_SAVE));
loaderList.add(new CopyLoader(ListKey.FILE_SIZE));
loaderList.add(new CopyLoader(ListKey.FILE_ALIGNMENT));
loaderList.add(new CopyLoader(ListKey.FILE_PCC));
loaderList.add(new SelfCopyLoader());
return loaderList;
}
private void ensureParents(File parentFile)
{
if (!parentFile.exists())
{
ensureParents(parentFile.getParentFile());
parentFile.mkdir();
}
}
private File findSubRoot(File root, File in)
{
File parent = in.getParentFile();
if (parent == null)
{
return null;
}
if (parent.getAbsolutePath().equals(root.getAbsolutePath()))
{
return parent;
}
return findSubRoot(root, parent);
}
private String load(URI uri, Loader loader) throws InterruptedException,
PersistenceLayerException
{
StringBuilder dataBuffer;
context.setSourceURI(uri);
context.setExtractURI(uri);
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 null;
}
StringBuilder resultBuffer = new StringBuilder(dataBuffer.length());
final String aString = dataBuffer.toString();
String[] fileLines = aString.split(LstFileLoader.LINE_SEPARATOR_REGEXP);
for (int line = 0; line < fileLines.length; line++)
{
String lineString = fileLines[line];
if ((lineString.isEmpty())
|| (lineString.charAt(0) == LstFileLoader.LINE_COMMENT_CHAR)
|| lineString.startsWith("SOURCE"))
{
resultBuffer.append(lineString);
}
else
{
List<CDOMObject> newObj = loader.process(resultBuffer, line,
lineString, decider);
if (newObj != null)
{
for (CDOMObject cdo : newObj)
{
injected.addToListFor(loader, uri, cdo);
}
}
}
resultBuffer.append("\n");
}
return resultBuffer.toString();
}
public Collection<Loader> getInjectedLoaders()
{
return injected.getKeySet();
}
public Collection<URI> getInjectedURIs(Loader l)
{
return injected.getSecondaryKeySet(l);
}
public Collection<CDOMObject> getInjectedObjects(Loader l, URI uri)
{
return injected.getListFor(l, uri);
}
}