I understand linux can be installed on machine having less physical memory. Is there any way to read 4GB log file using 1GB RAM? Basically I want to read few lines from large log file. That line can be anywhere in large log file.
-If someone can help me to understand linux tail implementation I can try same in java.
-At least which java I/O package should I use to read lines on demand, rather loading complete file on physical memory and then reading line by line.
I think you can use something like this..!!!
/**
* TODO Description go here.
* #author Rajakrishna V. Reddy
* #version 1.0
*/
#InterfaceAudience.Public
#InterfaceStability.Evolving
public abstract class FileTailer extends AbstractFileNotificationListener
{
/** The logger to log the debugging messages as application runs. */
private final Logger logger;
/** The tail text. */
private final StringBuilder tailText = new StringBuilder(256);
/** The file watcher. */
private final FileWatcher fileWatcher;
/**
* Instantiates a new file tailer.
*
* #param logFileName
* the log file name
* #throws FileNotFoundException
* the file not found exception
*/
public FileTailer(String logFileName) throws FileNotFoundException
{
logger = Logger.getLogger(FileTailer.class);
fileWatcher = FileWatcher.getFileWatcher();
fileWatcher.registerFileNotificationListener(this, logFileName);
fileWatcher.start();
}
/*
* (non-Javadoc)
*
* #see
* com.mt.filewatcher.listener.AbstractFileNotificationListener#onModifyFile
* (com.mt.filewatcher.info.FileInfo, com.mt.filewatcher.info.FileInfo)
*/
#Override
public final synchronized void onModifyFile(FileInfo oldFileInfo, FileInfo newFileInfo)
{
tailText.setLength(0);
final String name = oldFileInfo.getAbsolutePath();
RandomAccessFile randomFile = null;
try
{
randomFile = new RandomAccessFile(new File(name), "r");
final long offset = getOffset(oldFileInfo, newFileInfo);
if (offset > 0)
{
randomFile.seek(offset-1); // use native seek method to skip bytes
}
byte[] bytes = new byte[1024];
int noOfRead = -1;
while ((noOfRead = randomFile.read(bytes)) > 0) // Best approach than reading line by line.
{
for (int i = 0; i < noOfRead; i++)
{
tailText.append((char)bytes[i]);
}
bytes = new byte[1024];
}
final String tailedText = tailText.toString();
tail(tailedText);
if (tailedText != null && tailedText.contains("\n"))
{
final String[] lines = tailedText.split("\n");
for (String line : lines)
{
tailLineByLine(line);
}
}
else
{
tailLineByLine(tailedText);
}
}
catch (IOException e)
{
logger.error("Exception in tailing: ", e);
}
finally
{
if (randomFile != null)
{
try
{
randomFile.close();
}
catch (IOException e)
{
logger.error("Exception in closing the file: ", e);
}
}
}
}
/**
* Gets the tailed text.
* #see #tailLineByLine(String)
*
* #param tailedText
* the tailed text
*/
public abstract void tail(String tailedText);
/**
* Gets the line by line of the tailed text of the file as it is appending
* into file.
* <br><b>Note: This line does not contain the new line character.</b>
*
* #param tailedLine
* the tailed line
*/
public void tailLineByLine(String tailedLine)
{
//TODO
}
/**
* Gets the offset of the given old FileInfo.
*
* #param oldFileInfo
* the old file info
* #param newFileInfo
* the new file info
* #return the offset
*/
protected long getOffset(FileInfo oldFileInfo, FileInfo newFileInfo)
{
if (newFileInfo.getSize() < oldFileInfo.getSize())
{
return -1;
}
return oldFileInfo.getSize();
}
/**
* The main method.
*
* #param args
* the args
* #throws FileNotFoundException
* the file not found exception
*/
public static void main(String[] args) throws FileNotFoundException
{
System.setProperty(LoggerConstants.LOG_CLASS_FQ_NAME, ConsoleLogger.class.getName());
//new FileTailer("/krishna/Project/Hyperic/hq-hq/tools/unit_tests").onModifyFile(new FileInfo("/krishna/Project/Hyperic/hq-hq/tools/unit_tests/Raja/hi.txt"), null);
}
}
Related
I have a recursive method that steps through a large directory containing thousands of music files. It adds a music file to an observableList<> each time the extension meets the criteria. The list is hooked into a TableView<> in a different thread prior to the recursive method executing so that the user can see the files being added to the TableView<> in real time.
The problem is I know very little about how to manage memory in java and think I might be getting in the way of garbage collection. The recursive method eats up almost 6 GB of ram after around 3,000 songs and then begins to ignore files that it should be able to read. Furthermore, after it has 'finished' stepping through the directory structure, the ram does not reduce, (i.e. the stack from the recursive method is not being destroyed and I think all the objects that are referenced are still in heap memory).
It goes further.. I export the playlist to an XML file and close the program. When I relaunch it, the memory is completely reasonable, so i know its not the large list containing the files, it must have something to do with the recursive method.
Here's the recusive method located in the music handler:
/**
* method used to seek all mp3 files in a specified directory and save them
* to an ObservableArrayList
*
* #param existingSongs
* #param directory
* #return
* #throws FileNotFoundException
* #throws UnsupportedEncodingException
*/
protected ObservableList<FileBean> digSongs(ObservableList<FileBean> existingSongs,
File directory) throws FileNotFoundException,
UnsupportedEncodingException {
/*
* Each directory is broken into a list and passed back into the digSongs().
*/
if (directory.isDirectory() && directory.canRead()) {
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++) {
digSongs(existingSongs, files[i]);
}
/*
* if a file is not a directory, then is it checked to see if it's
* an mp3 file
*/
} else if (directory.getAbsolutePath().endsWith(".mp3")
|| directory.getAbsolutePath().endsWith(".m4a")
) {
FileBean songBean = new FileBean(directory).getSerializableJavaBean();
existingSongs.add(songBean);
songBean.getPlayer().setOnReady(new OnMediaReadyEvent(songBean));
songBean.getPlayer().setOnError(new OnMediaPlayerStalled(existingSongs, songBean));
/*
* if it's not a directory or mp3 file, then do nothing
*/
} else {
return existingSongs;
}
return existingSongs;
}
Here is the listener for the MediaPlayer used to read thr ID tags if possible, this is also located in the music handler
/**
* This class will populate the FileBean metaData after the MediaPlayer's
* status has been changed to READY. Uses the FileBean's setter methods so
* that they will be picked up by the XMLEncoder. This allows the use of the
* Media's ID3v2 tag reading abilities. If tags are not read due to
* incompatibility, they are not changed.
*
* This step is computationally expensive but should not need to be done
* very often and it saves a ton of memory during normal use. Setting the
* Media and MediaPlayer objects to null make this run much faster and uses
* less memory
*
* #author Karottop
*
*/
protected class OnMediaReadyEvent implements Runnable {
private FileBean fileBean;
public OnMediaReadyEvent(FileBean fileBean) {
this.fileBean = fileBean;
}
#Override
public void run() {
String songName = null;
String album = null;
String artist = null;
double duration = 0.0;
try{
// Retrieve track song title
songName = (String) fileBean.getMedia().getMetadata()
.get("title");
// Retrieve Album title
album = (String) fileBean.getMedia().getMetadata()
.get("album");
// Retrieve Artist title
artist = (String) fileBean.getMedia().getMetadata()
.get("artist");
// Retrieve Track duration
duration = fileBean.getMedia().getDuration().toMinutes();
}catch(NullPointerException e){
System.out.println(e.getMessage());
}
// Set track song title
if (songName != null)
fileBean.setSongName(songName);
// Set Album title
if (album != null)
fileBean.setAlbum(album);
// Retrieve and set Artist title
if (artist != null)
fileBean.setArtist(artist);
// Set Track duration
fileBean.setDuration(Double.parseDouble(
XMLMediaPlayerHelper.convertDecimalMinutesToTimeMinutes(duration)));
fileBean.setMedia(null);
fileBean.setPlayer(null);
}
}
Here is where I call the method in the controller for the FXML:
public class LoadAllMusicFiles implements Runnable{
private TableView<FileBean> tableView;
public LoadAllMusicFiles(TableView<FileBean> tableView) {
this.tableView = tableView;
}
#Override
public void run() {
try {
musicHandler.loadAllPlaylists();
tableView.setItems(musicHandler.getMainPlaylist().getSongsInPlaylist());
playlistTable.setItems(musicHandler.getPlaylists());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoPlaylistsFoundException e) {
String title = "Mine for mp3s";
String header = "No playlists were found.\n"
+ "These are your mp3 mining options...";
String content = "Do you want to import a single mp3\n"
+ "or a folder containing many mp3s?\n\n"
+ "**Note For large volumes of songs this may take a while.\n"
+ "Grab some coffee or something..**";
findNewSongs(title, header, content);
// need to handle file not found exception in new thread
tableView.setItems(musicHandler.getMainPlaylist().getSongsInPlaylist());
playlistTable.setItems(musicHandler.getPlaylists());
Platform.runLater(new SelectIndexOnTable(playlistTable, 0));
tableView.getSelectionModel().selectFirst();
}
}
}
/**
* The method will display an Alert box prompting the user to locate a
* song or directory that contains mp3s
*
* The parameters passed is the text the user will see in the Alert box.
* The Alert box will come with 3 new buttons: 1)Single mp3, 2)Folder of mp3s
* and 3)Cancel. If the user selects the first button they will be
* presented with a FileChooser display to select a song. If they press
* the second button, the user will be prompted with a DirectoryChooser
* display. The third button displays nothing and closes the Alert box.
*
* The following outlines where each parameter will be displayed in the
* Alert box
*
* title: very top of the box in the same latitude as the close button.
* header: inside the Alert box at the top.
* content: in the middle of the box. This is the best place to explain
* the button options to the user.
* #param title
* #param header
* #param content
*/
private void findNewSongs(String title, String header, String content){
Alert importType = new Alert(AlertType.CONFIRMATION);
importType.setTitle(title);
importType.setHeaderText(header);
importType.setContentText(content);
ButtonType singleMp3 = new ButtonType("Single mp3");
ButtonType folderOfmp3s = new ButtonType("Folder Of mp3s");
ButtonType cancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
importType.getButtonTypes().setAll(singleMp3, folderOfmp3s, cancel);
Optional<ButtonType> result = importType.showAndWait();
if(result.get() == singleMp3){
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Location of mp3s");
ArrayList<String> extensions = new ArrayList<>();
extensions.add("*.mp3");
fileChooser.getExtensionFilters().add(
new ExtensionFilter("Audio Files", getSupportedFileTypes()));
File selectedFile = fileChooser.showOpenDialog(playBackButton.getScene().getWindow());
if(selectedFile == null){
return;
}
Thread findSongs = new Thread(new DigSongs(selectedFile.getAbsolutePath()));
findSongs.start();
}else if(result.get() == folderOfmp3s){
DirectoryChooser fileChooser = new DirectoryChooser();
fileChooser.setTitle("Location to mine for mp3s");
File selectedFile = fileChooser.showDialog(playBackButton.getScene().getWindow());
if(selectedFile == null){
return;
}
Thread findSongs = new Thread(new DigSongs(selectedFile.getAbsolutePath()));
findSongs.start();
}else{
return;
}
}
public class DigSongs implements Runnable{
String path;
public DigSongs(String path) {
this.path = path;
}
#Override
public void run() {
Platform.runLater(new UpdateLabel(digLabel, "loading..."));
try {
musicHandler.findNewSongs(path);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ObservableList<FileBean> songArray = musicHandler.getMainPlaylist().getSongsInPlaylist();
Platform.runLater(new UpdateLabel(digLabel, "complete: " + songArray.size()));
}
}
This method is located in the music handler and basically just calls the recursive method digSongs(ObservableList, File):
/**
* This method will search for songs in a new directory and add them to the song list
* in the main playlist
* #param newDirectory
* #return
* #throws FileNotFoundException
* #throws UnsupportedEncodingException
*/
public PlaylistBean findNewSongs(String newDirectory)
throws FileNotFoundException, UnsupportedEncodingException{
PlaylistBean main = getMainPlaylist();
File file = new File(newDirectory);
// add new songs to existing main playlist
digSongs(main.getSongsInPlaylist(), file);
return main;
}
Guys, I know this is a lot of code and stuff to read. I just can't seem to find the answers I need on google. I suspect the problem has something to do with the reference being passed to the TableView<> but I honestly don't know. I hope someone can take the time to look. I'll post more code if anyone needs it
EDIT: FileBean class
package fun.personalUse.dataModel;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Comparator;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
/**
* Data model for use with a media player. This object is intended to store
* song data for 1 song
* #author Karottop
*
*/
public class FileBean implements Comparator<FileBean>, Comparable<FileBean>{
private File file;
private SimpleStringProperty location;
private SimpleStringProperty songName;
private SimpleStringProperty album;
private SimpleStringProperty artist;
private SimpleStringProperty url;
private Media media;
private MediaPlayer player;
private SimpleStringProperty duration;
/**
* inserts default or null values for every field. This constructor
* should be used when making a serializable FileBean. setters should
* be used to initialize the object
*/
public FileBean(){
media = null;
file = null;
location = new SimpleStringProperty();
songName = new SimpleStringProperty();
album = new SimpleStringProperty();
artist = new SimpleStringProperty();
url = new SimpleStringProperty();
/**
* must initialize with a number because this field will be called
* before the MediaPlayer's status has changed which would cause a
* null pointer exception to be thrown if not initialized
*/
duration = new SimpleStringProperty("0.0");
}
/**
* Initializes the file bean using a file
* #param file
* #throws FileNotFoundException
* #throws UnsupportedEncodingException
*/
public FileBean(File file) throws FileNotFoundException, UnsupportedEncodingException{
location = new SimpleStringProperty();
songName = new SimpleStringProperty();
album = new SimpleStringProperty();
artist = new SimpleStringProperty();
url = new SimpleStringProperty();
/**
* must initialize with a number because this field will be called
* before the MediaPlayer's status has changed which would cause a
* null pointer exception to be thrown if not initialized
*/
duration = new SimpleStringProperty("0.0");
this.file = file;
location.set(file.getAbsolutePath().replace("\\", "/"));
/*
* encode all special characters.
* URLEncoder puts a '+' where a ' ' is so change all '+' to encoded space '%20'.
*/
url.set(URLEncoder.encode(location.get(), "UTF-8").replace("+", "%20"));
/*
* Could not easily figure out how to set an action event for when the Media
* object is done loading. Using the MediaPlayer status change event instead.
* Looking for a better option
*/
media = new Media("file:///" + url.get());
this.player = new MediaPlayer(media);
setDefaultSongNameAndArtist();
}
public FileBean(String absolutePath) throws FileNotFoundException, UnsupportedEncodingException{
this(new File(absolutePath));
}
/**
* This method uses the parent directory strucutre to guesstimate
* what the song name, artist and album name is. a '?' is appended at the
* end of each item to indicate this is a guessed value
*
* media file that do not adhere to the following directory structure
* will not be named correctly:
*
* pathToMedia/Artist/Album/song
*/
private void setDefaultSongNameAndArtist(){
String[] songLocation = getLocation().split("/");
String[] songFragment = songLocation[songLocation.length - 1].split("[.]");
setSongName(songFragment[0]);
setAlbum(songLocation[songLocation.length - 2] + "?");
setArtist(songLocation[songLocation.length - 3] + "?");
}
/**
* #return the player
*/
public MediaPlayer getPlayer() {
return player;
}
/**
* #param player the player to set
*/
public void setPlayer(MediaPlayer player) {
this.player = player;
}
/**
* #return the duration
*/
public double getDuration() {
return Double.parseDouble(duration.get());
}
/**
* #param duration the duration to set
*/
public void setDuration(double duration) {
this.duration.set(String.format("%.2f", duration));
}
/**
* #return the album
*/
public String getAlbum() {
return album.get();
}
/**
* #param album the album to set
*/
public void setAlbum(String album) {
this.album.set(album);
}
/**
* #return the artist
*/
public String getArtist() {
return artist.get();
}
/**
* #param artist the artist to set
*/
public void setArtist(String artist) {
this.artist.set(artist);
}
/**
* #return the media
*/
public Media getMedia() {
return media;
}
/**
* #param media the media to set
*/
public void setMedia(Media media) {
this.media = media;
}
/**
* #return the url
*/
public String getUrl() {
return url.get();
}
/**
* #param url the url to set
*/
public void setUrl(String url) {
this.url.set(url);
}
/**
* #return the file
*/
public File getFile() {
return file;
}
/**
* #param file the file to set
*/
public void setFile(File file) {
this.file = file;
}
/**
* #return the location
*/
public String getLocation() {
return location.get();
}
/**
* #param location the location to set
*/
public void setLocation(String location) {
this.location.set(location);
}
/**
* #return the name
*/
public String getSongName() {
return songName.get();
}
/**
* #param name the name to set
*/
public void setSongName(String name) {
this.songName.set(name);
}
/**
* returns the songName property
* #return
*/
public SimpleStringProperty songNameProperty(){
return songName;
}
/**
* returns the artist property
* #return
*/
public SimpleStringProperty artistProperty(){
return artist;
}
/**
* returns the album property
* #return
*/
public SimpleStringProperty albumProperty(){
return album;
}
/**
* returns the duration property
* #return
*/
public SimpleStringProperty durationProperty(){
return duration;
}
/**
* Creates a serializable copy of this object
* by using it's setters. The purpose of this
* method is so that the FileBean objects can
* be exported to an XML
* #return
*/
public FileBean getSerializableJavaBean(){
FileBean temp = new FileBean();
temp.setAlbum(this.getAlbum());
temp.setArtist(this.getArtist());
temp.setDuration(this.getDuration());
temp.setFile(this.getFile());
temp.setLocation(this.getLocation());
temp.setMedia(this.getMedia());
temp.setPlayer(player);
temp.setSongName(this.getSongName());
temp.setUrl(this.getUrl());
return temp;
}
/**
* Method used to return a fully populated FileBean after decoded from XML.
* #return
*/
public FileBean getFullFileBean(){
try {
return new FileBean(new File(getLocation()));
} catch (FileNotFoundException | UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
FileBean temp = new FileBean();
temp.setLocation("error");
return temp;
}
}
/**
* Returns are string in the following format:
*
* [song name], [artist name], [album name]
*/
#Override
public String toString(){
return String.format("%s, %s, %s", getSongName(), getArtist(), getAlbum());
}
/**
* uses FileBean.toSting().compareTo(this.toString()) to determine if the two
* beans are equal
*/
#Override
public boolean equals(Object fileBean){
FileBean newBean = (FileBean)fileBean;
return newBean.toString().compareTo(this.toString()) == 0;
}
/**
* Uses the String.compare() to order FileBeans based on their absolute path
*/
#Override
public int compareTo(FileBean bean) {
if(this.getLocation().compareTo(bean.getLocation()) > 0){
return 1;
}else if(this.getLocation().compareTo(bean.getLocation()) < 0){
return -1;
} else{
return 0;
}
}
/**
* uses the compareTo method to compare two files beans.
*
* This method uses the String.compare() to order FileBeans
* based on their absolute path
*/
#Override
public int compare(FileBean bean1, FileBean bean2) {
// TODO Auto-generated method stub
return bean1.compareTo(bean2);
}
}
It is almost always a bad idea to use File.listFiles() because it eagerly allocates an array of files which may be very memory consuming.
So recursive digSongs method may produce significant peak memory usage (or even lead to OutOfMemoryError).
Take a look at Files.walkFileTree(...). It is a great memory efficient solution for directory traversal.
I tried to save data to json string in a txt file using Gson and then restore it using Gson either. Things go well if I do it in eclipse. But when packaged to jar, Gson throws Exceptions.
Here is the code for saving the file.
String gsonStr = gson.toJson(masterShips); // masterShips is ArrayList<Ship>
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter("D:\\master_ship.txt"));
writer.write(gsonStr);
} catch (IOException e) {
System.err.println(e);
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
System.err.println(e);
}
}
}
Then I read the file in eclipse using this code (and it works):
Scanner in = new Scanner(new FileReader("D:\\master_ship.txt"));
String str = in.nextLine();
Log.toDebug(str);
in.close();
JsonParser parser = new JsonParser();
JsonElement je = parser.parse(str);
JsonArray ja = je.getAsJsonArray();
for (int i=0; i<ja.size(); ++i) {
...
}
But after packaged into jar and run in cmd, Exception occurs:
Exception in thread "main" com.google.gson.JsonSyntaxException: com.google.gson.
stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malform
ed JSON at line 1 column 4
at com.google.gson.JsonParser.parse(JsonParser.java:65)
at com.google.gson.JsonParser.parse(JsonParser.java:45)
at kan.util.Master.loadMasterShip(Master.java:44)
at kan.util.Master.load(Master.java:27)
at kan.Main.main(Main.java:22)
Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLeni
ent(true) to accept malformed JSON at line 1 column 4
at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1505)
at com.google.gson.stream.JsonReader.checkLenient(JsonReader.java:1386)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:531)
at com.google.gson.stream.JsonReader.peek(JsonReader.java:414)
at com.google.gson.JsonParser.parse(JsonParser.java:60)
... 4 more
According to the hint of the Exception, I changed my code and it still works in eclipse:
Scanner in = new Scanner(new FileReader("D:\\master_ship.txt"));
String str = in.nextLine();
in.close();
Reader reader = new StringReader(str);
JsonReader jr = new JsonReader(reader);
jr.setLenient(true);
JsonParser parser = new JsonParser();
JsonElement je = parser.parse(jr);
JsonArray ja = je.getAsJsonArray();
for (int i=0; i<ja.size(); ++i) {
...
}
But jar failed and throws
Exception in thread "main" java.lang.IllegalStateException: This is not a JSON A
rray.
at com.google.gson.JsonElement.getAsJsonArray(JsonElement.java:106)
at kan.util.Master.loadMasterShip(Master.java:58)
at kan.util.Master.load(Master.java:30)
at kan.Main.main(Main.java:22)
As suggested by Sotirios I cut the length of the arraylist down, and when I increase the number of ships to 4, things go wrong. Here is the json:
[{"id":1,"name":"睦月","type":2,"rank":2,"fuelMax":15,"bulletMax":15,"slotNum":2,"speed":10,"afterLv":20,"afterId":254,"range":1,"powerups":[1,1,0,0]},{"id":2,"name":"如月","type":2,"rank":1,"fuelMax":15,"bulletMax":15,"slotNum":2,"speed":10,"afterLv":20,"afterId":255,"range":1,"powerups":[0,1,0,0]},{"id":6,"name":"長月","type":2,"rank":1,"fuelMax":15,"bulletMax":15,"slotNum":2,"speed":10,"afterLv":20,"afterId":258,"range":1,"powerups":[0,1,0,0]},{"id":7,"name":"三日月","type":2,"rank":1,"fuelMax":15,"bulletMax":15,"slotNum":2,"speed":10,"afterLv":20,"afterId":260,"range":1,"powerups":[0,1,0,0]}]
↑ colunm 473
Exception in thread "main" com.google.gson.JsonSyntaxException: com.google.gson.
stream.MalformedJsonException: Unterminated object at line 1 column 473
at com.google.gson.internal.Streams.parse(Streams.java:56)
at com.google.gson.JsonParser.parse(JsonParser.java:84)
at kan.util.Master.loadMasterShip(Master.java:55)
at kan.util.Master.load(Master.java:30)
at kan.Main.main(Main.java:22)
Caused by: com.google.gson.stream.MalformedJsonException: Unterminated object at
line 1 column 473
at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1505)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:480)
at com.google.gson.stream.JsonReader.hasNext(JsonReader.java:403)
at com.google.gson.internal.bind.TypeAdapters$25.read(TypeAdapters.java:
666)
at com.google.gson.internal.bind.TypeAdapters$25.read(TypeAdapters.java:
659)
at com.google.gson.internal.bind.TypeAdapters$25.read(TypeAdapters.java:
642)
at com.google.gson.internal.Streams.parse(Streams.java:44)
... 4 more
Can anyone help me with this, you will be reaally preciated!
Use this class
import java.util.List;
public class GsonResponse
{
public int id;
public String name;
public int type;
public int rank;
public int fuelMax;
public int bulletMax;
public int slotNum;
public int speed;
public int afterLv;
public int afterId;
public int range;
public List<Integer> powerups;
/**
* #return the id
*/
public int getId() {
return id;
}
/**
* #param id the id to set
*/
public void setId(int id) {
this.id = id;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* #return the type
*/
public int getType() {
return type;
}
/**
* #param type the type to set
*/
public void setType(int type) {
this.type = type;
}
/**
* #return the rank
*/
public int getRank() {
return rank;
}
/**
* #param rank the rank to set
*/
public void setRank(int rank) {
this.rank = rank;
}
/**
* #return the fuelMax
*/
public int getFuelMax() {
return fuelMax;
}
/**
* #param fuelMax the fuelMax to set
*/
public void setFuelMax(int fuelMax) {
this.fuelMax = fuelMax;
}
/**
* #return the bulletMax
*/
public int getBulletMax() {
return bulletMax;
}
/**
* #param bulletMax the bulletMax to set
*/
public void setBulletMax(int bulletMax) {
this.bulletMax = bulletMax;
}
/**
* #return the slotNum
*/
public int getSlotNum() {
return slotNum;
}
/**
* #param slotNum the slotNum to set
*/
public void setSlotNum(int slotNum) {
this.slotNum = slotNum;
}
/**
* #return the speed
*/
public int getSpeed() {
return speed;
}
/**
* #param speed the speed to set
*/
public void setSpeed(int speed) {
this.speed = speed;
}
/**
* #return the afterLv
*/
public int getAfterLv() {
return afterLv;
}
/**
* #param afterLv the afterLv to set
*/
public void setAfterLv(int afterLv) {
this.afterLv = afterLv;
}
/**
* #return the afterId
*/
public int getAfterId() {
return afterId;
}
/**
* #param afterId the afterId to set
*/
public void setAfterId(int afterId) {
this.afterId = afterId;
}
/**
* #return the range
*/
public int getRange() {
return range;
}
/**
* #param range the range to set
*/
public void setRange(int range) {
this.range = range;
}
/**
* #return the powerups
*/
public List<Integer> getPowerups() {
return powerups;
}
/**
* #param powerups the powerups to set
*/
public void setPowerups(List<Integer> powerups) {
this.powerups = powerups;
}
}
just add below code where u parse
String strJson = "[{\"id\":1,\"name\":\"睦月\",\"type\":2,\"rank\":2,\"fuelMax\":15,\"bulletMax\":15,\"slotNum\":2,\"speed\":10,\"afterLv\":20,\"afterId\":254,\"range\":1,\"powerups\":[1,1,0,0]},{\"id\":2,\"name\":\"如月\",\"type\":2,\"rank\":1,\"fuelMax\":15,\"bulletMax\":15,\"slotNum\":2,\"speed\":10,\"afterLv\":20,\"afterId\":255,\"range\":1,\"powerups\":[0,1,0,0]},{\"id\":6,\"name\":\"長月\",\"type\":2,\"rank\":1,\"fuelMax\":15,\"bulletMax\":15,\"slotNum\":2,\"speed\":10,\"afterLv\":20,\"afterId\":258,\"range\":1,\"powerups\":[0,1,0,0]},{\"id\":7,\"name\":\"三日月\",\"type\":2,\"rank\":1,\"fuelMax\":15,\"bulletMax\":15,\"slotNum\":2,\"speed\":10,\"afterLv\":20,\"afterId\":260,\"range\":1,\"powerups\":[0,1,0,0]}]";
GsonResponse gsonResponse = null;
try {
gsonResponse = new Gson().fromJson(strJson,
GsonResponse.class);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
I have a program that connects client threads to a cinema through a socket.
the client transfers his details (client no., required tickets) the cinema processes the request in another thread that makes sure there is enough seats available.
as you will see in the code, i have a slight problem with figuring out how to send a feedback to the client from the cinema(server), the code presented is not completed, i just wanted to give you an idea of how it looks.
my idea is to transfer the dataOutputStream to the cinemaThread and when its done, the cinema thread will return the feedback through the stream to the client is it possible?
Client side
import java.util.Random;
/**
* this class in designed to define client properties
* #author David and Adam
*
*/
public class Client extends Thread{
private int serialNumber;
private int NumberOfTickets;
private String creditNumber;
private String serverPort;
private int rowNumber;
/**
* full constructor
* #param serialNumber
* #param NumberOfTickets
* #param creditNumber
*/
public Client(int serialNumber){
this.serialNumber = serialNumber;
this.NumberOfTickets = generateTicketNumber();
this.creditNumber = generateCreditNumber();
this.rowNumber = -1;
}
/**
* returns a value in the required number of ticket range.
* #return
*/
private int generateTicketNumber(){
return (new Random()).nextInt(Constants.MaxNumberOfTickets-1)+Constants.MinNumberOfTickets;
}
/**
* returns a random credit number constructed of 16 digits.
* #return
*/
private String generateCreditNumber(){
String s = String.valueOf((new Random()).nextInt(9)+1);
for(int i=0 ; i<16 ; i++){
s = (new Random()).nextInt()+s;
}
return s;
}
Server side
import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* an implementation of server side defined as cinema hall
* #author David
*
*/
public class Cinema {
private int[][] cinemaHall;
private int ticketPrice;
private int securedPort;
private int numberOfRequests;
/**
* full constructor
*
*/
public Cinema(){
this.numberOfRequests = 0;
initializeCinemaHall();
setTicketPrice();
setSecuredPort();
}
/**
* initializes cinema hall to 0's
*/
private void initializeCinemaHall(){
this.cinemaHall = new int[10][10];
for(int i=0 ; i<cinemaHall.length ; i++)
for(int j=0 ; j<cinemaHall.length ; j++)
this.cinemaHall[i][j] = 0;
}
/**
* generates the ticket price on sale
*/
private void setTicketPrice(){
this.ticketPrice = Constants.TicketSalePrice;
}
/**
* sets the secured port
*
*/
private void setSecuredPort(){
this.securedPort = Constants.SecuredPort;
}
public int[][] getCinemaHall() {
return cinemaHall;
}
public void setCinemaHall(int[][] cinemaHall) {
this.cinemaHall = cinemaHall;
}
public int getTicketPrice() {
return ticketPrice;
}
public void setTicketPrice(int ticketPrice) {
this.ticketPrice = ticketPrice;
}
public int getNumberOfRequests() {
return numberOfRequests;
}
public void setNumberOfRequests(int numberOfRequests) {
this.numberOfRequests = numberOfRequests;
}
/**
* main function for establishing communication between cinema and customers
* #param args
* #throws IOException
*/
public static void main(String[] args) throws IOException{
Cinema cinema = new Cinema();
ServerSocket server = new ServerSocket(Constants.ServerPort);
System.out.println("**********Yes Planet server in listening**********");
// create a connection to clients
while(cinema.numberOfRequests<Constants.MaxClientNumber){
try {
//wait for client
Socket s = server.accept();
System.out.println("Client connected to socket");
// get client info
DataInputStream dis = new DataInputStream(s.getInputStream());
String clientInfo = dis.readLine();
String[] details = clientInfo.split("/");
try {
// parse data to correct type and send data to cinema thread
Thread cinemaThread = new CinemaThread(cinema, Integer.parseInt(details[0])
, Integer.parseInt(details[1]), details[2]);
cinemaThread.start();
}
catch (Exception e){
System.out.println("An error has occured, client request have been canceled");
}
} catch (IOException e) {
System.out.println("Connection error ");
e.printStackTrace();
}
}
}
}
Cinema Thread
public class CinemaThread extends Thread{
private int clientNumber;
private Cinema cinema;
private int requiredTickets;
private String clientCreditNumber;
private boolean occupied;
private int lineNumber;
private boolean alive;
/**
* full constructor
* #param clientNumber
* #param cinema
* #param requiredTickets
* #param clientCreditNumber
*/
public CinemaThread(Cinema cinema, int clientNumber, int requiredTickets, String clientCreditNumber){
this.clientNumber = clientNumber;
this.cinema = cinema;
this.requiredTickets = requiredTickets;
this.clientCreditNumber = clientCreditNumber;
this.occupied = false;
this.lineNumber = -1;
this.alive = true;
}
/**
* the method checks for available seats to each individual required client.
* in case an available sequence is found, cinema hall is updated with client details and a connection
* with the credit company is established forwarding the data needed to proceed.
*/
public void run(){
int ticketCount=0;
int startSeat = -1;
int endSeat = -1;
for(int i=0 ; i<cinema.getCinemaHall().length && !occupied; i++){
for(int j=0 ; j<cinema.getCinemaHall().length && !occupied; j++){
if(cinema.getCinemaHall()[i][j]>0)
ticketCount++;
else
ticketCount=0;
if(ticketCount == requiredTickets){
lineNumber = i;
startSeat = j-requiredTickets+1;
endSeat = j;
occupied=true;
}
}
for(int k=startSeat ;k<=endSeat ; k++)
cinema.getCinemaHall()[lineNumber][k] = clientNumber;
}
if(occupied){
// connection with credit company
}
this.alive = false;
}
public boolean status(){
return this.alive;
}
}
You don't need to transfer a DataOutputStream. Use Socket.getOutputStream().
server side
public static void main(String[] args) throws IOException{
// ... ...
// Socket s = server.accept();
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
// ... ...
Thread cinemaThread = new CinemaThread( /* ... */, dos);
// ... ...
}
public CinemaThread(/* ... */, DataOutputStream dos){
// ... ...
this.dos = dos;
}
public void run(){
// ... ...
if(occupied)
dos.writeBoolean(true);
else
dos.writeBoolean(false);
// ... ...
}
Streams are typically one way 'out' or 'in', so by my reading of the question, no it is not possible to use one stream for both.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Google Maps output=kml broken?
I started to get Error when I'm trying to retrieve Google Directions using KML data since few days. The Error seems that the URL I'm requesting doesn't retreieve KML data anymore, it returns a whole page. I followed this in order to achieve that.
What is the solution for this? Is there any alternatives?
Update:
Since Android Google Maps v2 is now used, the code needs to be adjusted to work on v2 maps which can be found here.
Original Answer:
This way of extracting the Google Directions from Google by parsing the KML file is no longer available since 27 July 2012 (because Google has changed the structure of retrieving Google Directions, now you can only get it by JSON or XML), it is time to migrate your code to JSON instead of KML.
I did it by creating 6 classes like this:
Parser.java:
public interface Parser {
public Route parse();
}
XMLParser.java:
public class XMLParser {
// names of the XML tags
protected static final String MARKERS = "markers";
protected static final String MARKER = "marker";
protected URL feedUrl;
protected XMLParser(final String feedUrl) {
try {
this.feedUrl = new URL(feedUrl);
} catch (MalformedURLException e) {
//Log.e(e.getMessage(), "XML parser - " + feedUrl);
}
}
protected InputStream getInputStream() {
try {
return feedUrl.openConnection().getInputStream();
} catch (IOException e) {
//Log.e(e.getMessage(), "XML parser - " + feedUrl);
return null;
}
}
}
Segment.java:
public class Segment {
/** Points in this segment. **/
private GeoPoint start;
/** Turn instruction to reach next segment. **/
private String instruction;
/** Length of segment. **/
private int length;
/** Distance covered. **/
private double distance;
/**
* Create an empty segment.
*/
public Segment() {
}
/**
* Set the turn instruction.
* #param turn Turn instruction string.
*/
public void setInstruction(final String turn) {
this.instruction = turn;
}
/**
* Get the turn instruction to reach next segment.
* #return a String of the turn instruction.
*/
public String getInstruction() {
return instruction;
}
/**
* Add a point to this segment.
* #param point GeoPoint to add.
*/
public void setPoint(final GeoPoint point) {
start = point;
}
/** Get the starting point of this
* segment.
* #return a GeoPoint
*/
public GeoPoint startPoint() {
return start;
}
/** Creates a segment which is a copy of this one.
* #return a Segment that is a copy of this one.
*/
public Segment copy() {
final Segment copy = new Segment();
copy.start = start;
copy.instruction = instruction;
copy.length = length;
copy.distance = distance;
return copy;
}
/**
* #param length the length to set
*/
public void setLength(final int length) {
this.length = length;
}
/**
* #return the length
*/
public int getLength() {
return length;
}
/**
* #param distance the distance to set
*/
public void setDistance(double distance) {
this.distance = distance;
}
/**
* #return the distance
*/
public double getDistance() {
return distance;
}
}
Route.java:
public class Route {
private String name;
private final List<GeoPoint> points;
private List<Segment> segments;
private String copyright;
private String warning;
private String country;
private int length;
private String polyline;
public Route() {
points = new ArrayList<GeoPoint>();
segments = new ArrayList<Segment>();
}
public void addPoint(final GeoPoint p) {
points.add(p);
}
public void addPoints(final List<GeoPoint> points) {
this.points.addAll(points);
}
public List<GeoPoint> getPoints() {
return points;
}
public void addSegment(final Segment s) {
segments.add(s);
}
public List<Segment> getSegments() {
return segments;
}
/**
* #param name the name to set
*/
public void setName(final String name) {
this.name = name;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param copyright the copyright to set
*/
public void setCopyright(String copyright) {
this.copyright = copyright;
}
/**
* #return the copyright
*/
public String getCopyright() {
return copyright;
}
/**
* #param warning the warning to set
*/
public void setWarning(String warning) {
this.warning = warning;
}
/**
* #return the warning
*/
public String getWarning() {
return warning;
}
/**
* #param country the country to set
*/
public void setCountry(String country) {
this.country = country;
}
/**
* #return the country
*/
public String getCountry() {
return country;
}
/**
* #param length the length to set
*/
public void setLength(int length) {
this.length = length;
}
/**
* #return the length
*/
public int getLength() {
return length;
}
/**
* #param polyline the polyline to set
*/
public void setPolyline(String polyline) {
this.polyline = polyline;
}
/**
* #return the polyline
*/
public String getPolyline() {
return polyline;
}
}
GoogleParser.java:
public class GoogleParser extends XMLParser implements Parser {
/** Distance covered. **/
private int distance;
public GoogleParser(String feedUrl) {
super(feedUrl);
}
/**
* Parses a url pointing to a Google JSON object to a Route object.
* #return a Route object based on the JSON object.
*/
public Route parse() {
// turn the stream into a string
final String result = convertStreamToString(this.getInputStream());
//Create an empty route
final Route route = new Route();
//Create an empty segment
final Segment segment = new Segment();
try {
//Tranform the string into a json object
final JSONObject json = new JSONObject(result);
//Get the route object
final JSONObject jsonRoute = json.getJSONArray("routes").getJSONObject(0);
//Get the leg, only one leg as we don't support waypoints
final JSONObject leg = jsonRoute.getJSONArray("legs").getJSONObject(0);
//Get the steps for this leg
final JSONArray steps = leg.getJSONArray("steps");
//Number of steps for use in for loop
final int numSteps = steps.length();
//Set the name of this route using the start & end addresses
route.setName(leg.getString("start_address") + " to " + leg.getString("end_address"));
//Get google's copyright notice (tos requirement)
route.setCopyright(jsonRoute.getString("copyrights"));
//Get the total length of the route.
route.setLength(leg.getJSONObject("distance").getInt("value"));
//Get any warnings provided (tos requirement)
if (!jsonRoute.getJSONArray("warnings").isNull(0)) {
route.setWarning(jsonRoute.getJSONArray("warnings").getString(0));
}
/* Loop through the steps, creating a segment for each one and
* decoding any polylines found as we go to add to the route object's
* map array. Using an explicit for loop because it is faster!
*/
for (int i = 0; i < numSteps; i++) {
//Get the individual step
final JSONObject step = steps.getJSONObject(i);
//Get the start position for this step and set it on the segment
final JSONObject start = step.getJSONObject("start_location");
final GeoPoint position = new GeoPoint((int) (start.getDouble("lat")*1E6),
(int) (start.getDouble("lng")*1E6));
segment.setPoint(position);
//Set the length of this segment in metres
final int length = step.getJSONObject("distance").getInt("value");
distance += length;
segment.setLength(length);
segment.setDistance(distance/1000);
//Strip html from google directions and set as turn instruction
segment.setInstruction(step.getString("html_instructions").replaceAll("<(.*?)*>", ""));
//Retrieve & decode this segment's polyline and add it to the route.
route.addPoints(decodePolyLine(step.getJSONObject("polyline").getString("points")));
//Push a copy of the segment to the route
route.addSegment(segment.copy());
}
} catch (JSONException e) {
Log.e(e.getMessage(), "Google JSON Parser - " + feedUrl);
}
return route;
}
/**
* Convert an inputstream to a string.
* #param input inputstream to convert.
* #return a String of the inputstream.
*/
private static String convertStreamToString(final InputStream input) {
final BufferedReader reader = new BufferedReader(new InputStreamReader(input));
final StringBuilder sBuf = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sBuf.append(line);
}
} catch (IOException e) {
Log.e(e.getMessage(), "Google parser, stream2string");
} finally {
try {
input.close();
} catch (IOException e) {
Log.e(e.getMessage(), "Google parser, stream2string");
}
}
return sBuf.toString();
}
/**
* Decode a polyline string into a list of GeoPoints.
* #param poly polyline encoded string to decode.
* #return the list of GeoPoints represented by this polystring.
*/
private List<GeoPoint> decodePolyLine(final String poly) {
int len = poly.length();
int index = 0;
List<GeoPoint> decoded = new ArrayList<GeoPoint>();
int lat = 0;
int lng = 0;
while (index < len) {
int b;
int shift = 0;
int result = 0;
do {
b = poly.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lat += dlat;
shift = 0;
result = 0;
do {
b = poly.charAt(index++) - 63;
result |= (b & 0x1f) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
lng += dlng;
decoded.add(new GeoPoint(
(int) (lat*1E6 / 1E5), (int) (lng*1E6 / 1E5)));
}
return decoded;
}
}
RouteOverlay.java:
public class RouteOverlay extends Overlay {
/** GeoPoints representing this routePoints. **/
private final List<GeoPoint> routePoints;
/** Colour to paint routePoints. **/
private int colour;
/** Alpha setting for route overlay. **/
private static final int ALPHA = 120;
/** Stroke width. **/
private static final float STROKE = 4.5f;
/** Route path. **/
private final Path path;
/** Point to draw with. **/
private final Point p;
/** Paint for path. **/
private final Paint paint;
/**
* Public constructor.
*
* #param route Route object representing the route.
* #param defaultColour default colour to draw route in.
*/
public RouteOverlay(final Route route, final int defaultColour) {
super();
routePoints = route.getPoints();
colour = defaultColour;
path = new Path();
p = new Point();
paint = new Paint();
}
#Override
public final void draw(final Canvas c, final MapView mv,
final boolean shadow) {
super.draw(c, mv, shadow);
paint.setColor(colour);
paint.setAlpha(ALPHA);
paint.setAntiAlias(true);
paint.setStrokeWidth(STROKE);
paint.setStyle(Paint.Style.STROKE);
redrawPath(mv);
c.drawPath(path, paint);
}
/**
* Set the colour to draw this route's overlay with.
*
* #param c Int representing colour.
*/
public final void setColour(final int c) {
colour = c;
}
/**
* Clear the route overlay.
*/
public final void clear() {
routePoints.clear();
}
/**
* Recalculate the path accounting for changes to
* the projection and routePoints.
* #param mv MapView the path is drawn to.
*/
private void redrawPath(final MapView mv) {
final Projection prj = mv.getProjection();
path.rewind();
final Iterator<GeoPoint> it = routePoints.iterator();
prj.toPixels(it.next(), p);
path.moveTo(p.x, p.y);
while (it.hasNext()) {
prj.toPixels(it.next(), p);
path.lineTo(p.x, p.y);
}
path.setLastPoint(p.x, p.y);
}
}
And then you do this inside the Activity that includes the Map:
1-Add this function:
private Route directions(final GeoPoint start, final GeoPoint dest) {
Parser parser;
//https://developers.google.com/maps/documentation/directions/#JSON <- get api
String jsonURL = "http://maps.googleapis.com/maps/api/directions/json?";
final StringBuffer sBuf = new StringBuffer(jsonURL);
sBuf.append("origin=");
sBuf.append(start.getLatitudeE6()/1E6);
sBuf.append(',');
sBuf.append(start.getLongitudeE6()/1E6);
sBuf.append("&destination=");
sBuf.append(dest.getLatitudeE6()/1E6);
sBuf.append(',');
sBuf.append(dest.getLongitudeE6()/1E6);
sBuf.append("&sensor=true&mode=driving");
parser = new GoogleParser(sBuf.toString());
Route r = parser.parse();
return r;
}
2- Add this in onCreate() function:
MapView mapView = (MapView) findViewById(R.id.mapview); //or you can declare it directly with the API key
Route route = directions(new GeoPoint((int)(26.2*1E6),(int)(50.6*1E6)), new GeoPoint((int)(26.3*1E6),(int)(50.7*1E6)));
RouteOverlay routeOverlay = new RouteOverlay(route, Color.BLUE);
mapView.getOverlays().add(routeOverlay);
mapView.invalidate();
EDIT: If you get an exception, please use directions() function in an AsyncTask to avoid network processing on the UI thread.
I need to write List<MyClass> myList into CSV file. In particular I need to write values. MyClass has the following implementation:
public class MyClass {
private Object[] values;
//...
#Override
public String toString()
{
String line = "";
for (int i=0; i<this.getSize(); i++) {
//if (this.values[i] != null) {
line = line + this.values[i] + " ";
//}
}
return line;
}
}
The code is the following:
private void saveSolutionToCSV(List<MyClass> solution) {
int columnSize = solution.get(0).getSize();
try {
FileWriter writer = new FileWriter("test.csv");
Iterator result = solution.iterator();
while(result.hasNext()) {
for(int i = 0; i < columnSize; i++) {
CharSequence element = (CharSequence)result.next();
writer.append(element);
if(i < columnSize - 1)
writer.append(',');
}
writer.append('\n');
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
And the error message is the following:
java.lang.ClassCastException: myPackage.MyClass cannot be cast to java.lang.CharSequence
How to solve this problem? Thx.
Try:
String element = result.next().toString();
writer.append(element);
if(i < columnSize - 1)
writer.append(',');
You need to call toString() before casting to CharSequence.
Try calling the toString() method explicitly like
CharSequence element = (CharSequence)result.next().toString();
CsvWriter.java
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Collection;
/**
* Composes CSV data for collection of objects. Relays on theirs {#link Object#toString toString()} implementation.<br>
* Could use memory buffer for the data or write straight to the output stream.
* <p>
* Example 1:
*
* <pre>
* Object[][] data = ...
*
* CsvWriter writer = new CsvWriter();
* writer.write("Column 1", "Column 2", ... "Column N");
* for (Object[] values : data)
* writer.write(values);
* ...
* System.out.println(writer.toString());
* </pre>
* <p>
* Example 2:
*
* <pre>
* Object[][] data = ...
*
* CsvWriter writer = null;
* try {
* writer = new CsvWriter(new FileOutputStream(new File("data.csv")));
* writer.write("Column 1", "Column 2", ... "Column N");
* for (Object[] values : data)
* writer.write(values);
* } finally {
* writer.close();
* }
* </pre>
*
* #author Mykhaylo Adamovych
*/
public class CsvWriter implements Closeable {
public static final String NULL_MARK = "";
public static final String QUOTE = "\"";
private String nullMark = NULL_MARK;
private final PrintWriter pw;
private ByteArrayOutputStream baos;
/**
* Creates temporary buffer in the memory. <br>
* Use {#link #toString()} thereafter. No need to close.
*/
public CsvWriter() {
baos = new ByteArrayOutputStream();
pw = new PrintWriter(baos, true);
}
/**
* Doesn't consume memory for CSV data, writes straight to the output stream. Just like {#link FilterOutputStream}, but deal with objects and relays on
* theirs {#link Object#toString toString()} implementation.<br>
* Close writer thereafter.
*
* #param os
* output stream to write data.
*/
public CsvWriter(OutputStream os) {
pw = new PrintWriter(os, true);
}
protected String composeRecord(Object... values) {
if (values == null || values.length == 0)
return "";
final StringBuffer csvRecord = new StringBuffer();
csvRecord.append(QUOTE);
csvRecord.append(composeValue(values[0]));
csvRecord.append(QUOTE);
for (int i = 1; i < values.length; i++) {
csvRecord.append("," + QUOTE);
csvRecord.append(composeValue(values[i]));
csvRecord.append(QUOTE);
}
return csvRecord.toString();
}
protected String composeValue(Object value) {
if (value == null)
return nullMark;
return value.toString().replaceAll(QUOTE, QUOTE + QUOTE);
}
public String getNullMark() {
return nullMark;
}
public void setNullMark(String nullMarker) {
nullMark = nullMarker;
}
#Override
public String toString() {
if (baos == null)
throw new UnsupportedOperationException();
return baos.toString();
}
/**
* Writes collection of objects as CSV record.
*
* #param values
*/
public void write(Collection<?> values) {
write(values.toArray());
}
/**
* Writes collection of objects as CSV record.
*
* #param values
*/
public void write(Object... values) {
pw.println(composeRecord(values));
}
#Override
public void close() throws IOException {
if (baos != null)
throw new UnsupportedOperationException();
pw.close();
}
}
example:
public class MyClass {
private Object[][] data;
//...
#Override
public String toCsv() {
CsvWriter writer = new CsvWriter();
csvWriter.write("Column1", "Column2")
for (final Object[] values : data)
csvWriter.write(values);
return writer.toString();
}
}