Applied-Energistics-2-tiler.../src/main/java/appeng/services/export/MinecraftItemCSVExporter.java
thatsIch 888b3e5600 Closes #1899, Fixed #1898: Adds an easy way to export interesting information into CSV format
Mostly used for the recipe system, but can also be used for debugging purposes. Debug options needs to be ticked to use the full information gain. Recipes only require the normal localization and the specific name plus metadata.

Shifted the recipes into a recipes folder where the CSV will also reside. This will also elevate the copying of the readme to the user directory since it can reside in the recipes folder.

Fixed a bug where the copier would copy the would also copy empty folders
2015-12-23 14:32:53 +01:00

291 lines
9.5 KiB
Java

/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 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 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 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 Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.services.export;
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.nio.charset.Charset;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.StatCollector;
import cpw.mods.fml.common.registry.FMLControlledNamespacedRegistry;
import appeng.core.AELog;
/**
* handles the exporting including processing, transformation and persisting the information
*
* @author thatsIch
* @version rv3 - 14.08.2015
* @since rv3 14.08.2015
*/
final class MinecraftItemCSVExporter implements Exporter
{
private static final String ITEM_CSV_FILE_NAME = "items.csv";
private static final String MINIMAL_HEADER = "Mod:Item:MetaData, Localized Name";
private static final String VERBOSE_HEADER = MINIMAL_HEADER + ", Unlocalized Name, Is Air?, Class Name";
private static final String EXPORT_SUCCESSFUL_MESSAGE = "Exported successfully %d items into %s";
private static final String EXPORT_UNSUCCESSFUL_MESSAGE = "Exporting was unsuccessful.";
@Nonnull
private final File exportDirectory;
@Nonnull
private final FMLControlledNamespacedRegistry<Item> itemRegistry;
@Nonnull
private final ExportMode mode;
/**
* @param exportDirectory directory of the resulting export file. Non-null required.
* @param itemRegistry the registry with minecraft items. Needs to be populated at that time, thus the exporting can only happen in init (pre-init is the
* phase when all items are determined)
* @param mode mode in which the export should be operated. Resulting CSV will change depending on this.
*/
MinecraftItemCSVExporter( @Nonnull final File exportDirectory, @Nonnull final FMLControlledNamespacedRegistry<Item> itemRegistry, @Nonnull final ExportMode mode )
{
this.exportDirectory = Preconditions.checkNotNull( exportDirectory );
Preconditions.checkArgument( !exportDirectory.isFile() );
this.itemRegistry = Preconditions.checkNotNull( itemRegistry );
this.mode = Preconditions.checkNotNull( mode );
}
@Override
public void export()
{
final Iterable<Item> items = this.itemRegistry.typeSafeIterable();
final List<Item> itemList = Lists.newArrayList( items );
final List<String> lines = Lists.transform( itemList, new ItemRowExtractFunction( this.itemRegistry, this.mode ) );
final Joiner newLineJoiner = Joiner.on( '\n' );
final Joiner newLineJoinerIgnoringNull = newLineJoiner.skipNulls();
final String joined = newLineJoinerIgnoringNull.join( lines );
final File file = new File( this.exportDirectory, ITEM_CSV_FILE_NAME );
try
{
FileUtils.forceMkdir( this.exportDirectory );
final Writer writer = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( file ), Charset.forName("UTF-8") ) );
final String header = this.mode == ExportMode.MINIMAL ? MINIMAL_HEADER : VERBOSE_HEADER;
writer.write( header );
writer.write( "\n" );
writer.write( joined );
writer.flush();
writer.close();
AELog.info( EXPORT_SUCCESSFUL_MESSAGE, lines.size(), ITEM_CSV_FILE_NAME );
}
catch( final IOException e )
{
AELog.warning( EXPORT_UNSUCCESSFUL_MESSAGE );
AELog.error( e );
}
}
/**
* Extracts item name with meta and the display name
*/
private static final class TypeExtractFunction implements Function<ItemStack, String>
{
private static final String EXTRACTING_NULL_MESSAGE = "extracting type null";
private static final String EXTRACTING_ITEM_MESSAGE = "extracting type %s:%d";
@Nonnull
private final String itemName;
@Nonnull
private final ExportMode mode;
private TypeExtractFunction( @Nonnull final String itemName, @Nonnull final ExportMode mode )
{
this.itemName = Preconditions.checkNotNull( itemName );
Preconditions.checkArgument( !itemName.isEmpty() );
this.mode = Preconditions.checkNotNull( mode );
}
@Nullable
@Override
public String apply( @Nullable final ItemStack input )
{
if( input == null )
{
AELog.debug( EXTRACTING_NULL_MESSAGE );
return null;
}
else
{
AELog.debug( EXTRACTING_ITEM_MESSAGE, input.getDisplayName(), input.getItemDamage() );
}
final List<String> joinedBlockAttributes = Lists.newArrayListWithCapacity( 5 );
final int meta = input.getItemDamage();
final String metaName = this.itemName + ':' + meta;
final String localization = input.getDisplayName();
joinedBlockAttributes.add( metaName );
joinedBlockAttributes.add( localization );
if( this.mode == ExportMode.VERBOSE )
{
final Item item = input.getItem();
final String unlocalizedItem = input.getUnlocalizedName();
final Block block = Block.getBlockFromItem( item );
final boolean isBlock = !block.equals( Blocks.air );
final Class<? extends ItemStack> stackClass = input.getClass();
final String stackClassName = stackClass.getName();
joinedBlockAttributes.add( unlocalizedItem );
joinedBlockAttributes.add( Boolean.toString( isBlock ) );
joinedBlockAttributes.add( stackClassName );
}
final Joiner csvJoiner = Joiner.on( ", " );
final Joiner csvJoinerIgnoringNulls = csvJoiner.skipNulls();
return csvJoinerIgnoringNulls.join( joinedBlockAttributes );
}
}
/**
* transforms an item into a row representation of the CSV file
*/
private static final class ItemRowExtractFunction implements Function<Item, String>
{
/**
* this extension is required to apply the {@link StatCollector}
*/
private static final String LOCALIZATION_NAME_EXTENSION = ".name";
private static final String EXPORTING_NOTHING_MESSAGE = "Exporting nothing";
private static final String EXPORTING_SUBTYPES_MESSAGE = "Exporting input %s with subtypes: %b";
@Nonnull
private final FMLControlledNamespacedRegistry<Item> itemRegistry;
@Nonnull
private final ExportMode mode;
/**
* @param itemRegistry used to retrieve the name of the item
* @param mode extracts more or less information from item depending on mode
*/
ItemRowExtractFunction( @Nonnull final FMLControlledNamespacedRegistry<Item> itemRegistry, @Nonnull final ExportMode mode )
{
this.itemRegistry = Preconditions.checkNotNull( itemRegistry );
this.mode = Preconditions.checkNotNull( mode );
}
@Nullable
@Override
public String apply( @Nullable final Item input )
{
if( input == null )
{
AELog.debug( EXPORTING_NOTHING_MESSAGE );
return null;
}
else
{
AELog.debug( EXPORTING_SUBTYPES_MESSAGE, input.getUnlocalizedName(), input.getHasSubtypes() );
}
final String itemName = this.itemRegistry.getNameForObject( input );
final boolean hasSubtypes = input.getHasSubtypes();
if( hasSubtypes )
{
final CreativeTabs creativeTab = input.getCreativeTab();
final List<ItemStack> stacks = Lists.newArrayList();
// modifies the stacks list and adds the different sub types to it
try
{
input.getSubItems( input, creativeTab, stacks );
}
catch( final Exception ignored )
{
AELog.error( ignored );
// ignore if mods do bullshit in their code
return null;
}
// list can be empty, no clue why
if( stacks.isEmpty() )
{
return null;
}
final Joiner newLineJoiner = Joiner.on( '\n' );
final Joiner typeJoiner = newLineJoiner.skipNulls();
final List<String> transformedTypes = Lists.transform( stacks, new TypeExtractFunction( itemName, this.mode ) );
return typeJoiner.join( transformedTypes );
}
final List<String> joinedBlockAttributes = Lists.newArrayListWithCapacity( 5 );
final String unlocalizedItem = input.getUnlocalizedName();
final String localization = StatCollector.translateToLocal( unlocalizedItem + LOCALIZATION_NAME_EXTENSION );
joinedBlockAttributes.add( itemName );
joinedBlockAttributes.add( localization );
if( this.mode == ExportMode.VERBOSE )
{
final Block block = Block.getBlockFromItem( input );
final boolean isBlock = !block.equals( Blocks.air );
final Class<? extends Item> itemClass = input.getClass();
final String itemClassName = itemClass.getName();
joinedBlockAttributes.add( unlocalizedItem );
joinedBlockAttributes.add( Boolean.toString( isBlock ) );
joinedBlockAttributes.add( itemClassName );
}
final Joiner csvJoiner = Joiner.on( ", " );
final Joiner csvJoinerIgnoringNulls = csvJoiner.skipNulls();
return csvJoinerIgnoringNulls.join( joinedBlockAttributes );
}
}
}