return false;
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
- writer.append(toString(format));
+ Parser.write(writer, format, this);
writer.close();
if (this.file != null
return true;
}
- /**
- * Return a {@link String} representation of this {@link Card} in the given
- * {@link Format}.
- *
- * @param format
- * the {@link Format} to use
- *
- * @return the {@link String}
- */
- public String toString(Format format) {
- StringBuilder builder = new StringBuilder();
- for (String line : Parser.toStrings(this, format)) {
- builder.append(line);
- builder.append("\r\n");
- }
- return builder.toString();
- }
-
/**
* Return the name of this card (the name of the {@link File} which it was
* opened from).
return file;
}
+ /**
+ * Break the link between this {@link Card} and he {@link File} which was
+ * used to open it if any.
+ */
+ public void unlink() {
+ file = null;
+ lastModified = -1;
+ }
+
/**
* Return the date of the last modification for this {@link Card} (or -1 if
* unknown/new).
@Override
public String toString() {
- return toString(Format.VCard21);
+ return "[Card: " + name + "]";
}
@Override
return found;
}
- /**
- * Return a {@link String} representation of this contact.
- *
- * @param format
- * the {@link Format} to use
- * @param startingBKey
- * the starting BKey or -1 for no BKeys
- * @return the {@link String} representation
- */
- public String toString(Format format, int startingBKey) {
- updateBKeys(false);
-
- StringBuilder builder = new StringBuilder();
- for (String line : Parser.toStrings(this, format, startingBKey)) {
- builder.append(line);
- builder.append("\r\n");
- }
-
- return builder.toString();
- }
-
/**
* Return a {@link String} representation of this contact formated
* accordingly to the given format.
value = ff.get(fieldNum);
}
} else {
- value = getPreferredDataValue(field);
+ // we don't need the *data* in binary mode...
+ if (binary)
+ value = getData(field).size() > 0 ? "x" : null;
+ else
+ value = getPreferredDataValue(field);
}
if (value == null) {
*/
@Override
public String toString() {
- return toString(Format.VCard21, -1);
+ return "[Contact: " + getPreferredDataValue("FN") + "]";
}
/**
* @return the translated text with the given value where required
*/
static public String trans(StringId id, Object... values) {
- return transService.getString(id, (Object[]) values);
+ return transService.getString(id, values);
}
/**
new RemoteBundle().updateFile(dir);
} catch (IOException e) {
e.printStackTrace();
+ System.err.flush();
System.exit(ERR_INTERNAL);
}
break;
List<TypeInfo> types = new LinkedList<TypeInfo>();
types.add(new TypeInfo("ENCODING", "b"));
- types.add(new TypeInfo("TYPE", "png"));
+ types.add(new TypeInfo("TYPE", "jpeg"));
Data photo = new Data(types, "PHOTO", b64, null);
contact.add(photo);
} catch (IOException e) {
else
System.err.println(trans(err, trans(suberr, subvalues)));
+ System.err.flush();
System.exit(CODE);
}
}
package be.nikiroo.jvcard.parsers;
+import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
return lines;
}
+
+ /**
+ * Write the given {@link Contact} in the {@link Appendable}.
+ *
+ * @param writer
+ * the {@link Appendable}
+ * @param contact
+ * the {@link Contact} to write
+ * @param startingBKey
+ * the starting BKey number (all the other will follow) or -1 for
+ * no BKey
+ *
+ * @throws IOException
+ * in case of IO error
+ */
+ public static void write(Appendable writer, Contact contact,
+ int startingBKey) throws IOException {
+ for (String s : toStrings(contact, startingBKey)) {
+ writer.append(s);
+ writer.append('\n');
+ }
+ }
+
+ /**
+ * Write the given {@link Card} in the {@link Appendable}.
+ *
+ * @param writer
+ * the {@link Appendable}
+ * @param card
+ * the {@link Card} to write
+ *
+ * @throws IOException
+ * in case of IO error
+ */
+ public static void write(Appendable writer, Card card) throws IOException {
+ for (String s : toStrings(card)) {
+ writer.append(s);
+ writer.append('\n');
+ }
+ }
}
}
/**
- * Return a {@link String} representation of the given {@link Card}, line by
- * line.
+ * Write the given {@link Card} in the {@link Appendable}.
*
+ * @param writer
+ * the {@link Appendable}
* @param card
- * the card to convert
- *
+ * the {@link Card} to write
* @param format
- * the output {@link Format} to use
+ * the {@link Format} to export to
*
- * @return the {@link String} representation
+ * @throws IOException
+ * in case of IO error
*/
- public static List<String> toStrings(Card card, Format format) {
+ public static void write(Appendable writer, Format format, Card card)
+ throws IOException {
switch (format) {
case VCard21:
- return Vcard21Parser.toStrings(card);
+ Vcard21Parser.write(writer, card);
+ break;
case Abook:
- return AbookParser.toStrings(card);
-
+ AbookParser.write(writer, card);
+ break;
default:
throw new InvalidParameterException("Unknown format: "
+ format.toString());
}
/**
- * Return a {@link String} representation of the given {@link Card}, line by
- * line.
- *
- * @param card
- * the card to convert
+ * Write the given {@link Contact} in the {@link Appendable}.
*
+ * @param writer
+ * the {@link Appendable}
+ * @param contact
+ * the {@link Contact} to write
* @param startingBKey
* the starting BKey number (all the other will follow) or -1 for
* no BKey
- *
* @param format
- * the output {@link Format} to use
+ * the {@link Format} to export to
*
- * @return the {@link String} representation
+ * @throws IOException
+ * in case of IO error
*/
- public static List<String> toStrings(Contact contact, Format format,
- int startingBKey) {
+ public static void write(Appendable writer, Contact contact, Format format,
+ int startingBKey) throws IOException {
switch (format) {
case VCard21:
- return Vcard21Parser.toStrings(contact, startingBKey);
+ Vcard21Parser.write(writer, contact, startingBKey);
+ break;
case Abook:
- return AbookParser.toStrings(contact, startingBKey);
-
+ AbookParser.write(writer, contact, startingBKey);
+ break;
default:
throw new InvalidParameterException("Unknown format: "
+ format.toString());
package be.nikiroo.jvcard.parsers;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
}
/**
- * Return a {@link String} representation of the given {@link Card}, line by
- * line.
+ * Write the given {@link Card} in the {@link Appendable}.
*
+ * @param writer
+ * the {@link Appendable}
* @param card
- * the card to convert
+ * the {@link Card} to write
*
- * @return the {@link String} representation
+ * @throws IOException
+ * in case of IO error
*/
- public static List<String> toStrings(Card card) {
- List<String> lines = new LinkedList<String>();
-
+ public static void write(Appendable writer, Card card) throws IOException {
for (Contact contact : card) {
- lines.addAll(toStrings(contact, -1));
+ write(writer, contact, -1);
}
-
- return lines;
}
/**
- * Return a {@link String} representation of the given {@link Contact}, line
- * by line.
- *
- * @param card
- * the contact to convert
+ * Write the given {@link Contact} in the {@link Appendable}.
*
+ * @param writer
+ * the {@link Appendable}
+ * @param contact
+ * the {@link Contact} to write
* @param startingBKey
* the starting BKey number (all the other will follow) or -1 for
* no BKey
*
- * @return the {@link String} representation
+ * @throws IOException
+ * in case of IO error
*/
- public static List<String> toStrings(Contact contact, int startingBKey) {
- List<String> lines = new LinkedList<String>();
+ public static void write(Appendable writer, Contact contact,
+ int startingBKey) throws IOException {
- lines.add("BEGIN:VCARD");
- lines.add("VERSION:2.1");
+ writer.append("BEGIN:VCARD\r\n");
+ writer.append("VERSION:2.1\r\n");
for (Data data : contact) {
- lines.addAll(toStrings(data));
+ write(writer, data);
}
- lines.add("END:VCARD");
-
- return lines;
+ writer.append("END:VCARD\r\n");
}
/**
- * Return a {@link String} representation of the given {@link Data}, line by
- * line.
+ * Write the given {@link Data} in the {@link Appendable}.
*
+ * @param writer
+ * the {@link Appendable}
* @param data
- * the data to convert
+ * the {@link Data} to write
*
- * @return the {@link String} representation
+ * @throws IOException
+ * in case of IO error
*/
- public static List<String> toStrings(Data data) {
- List<String> lines = new LinkedList<String>();
-
+ public static void write(Appendable writer, Data data) throws IOException {
StringBuilder dataBuilder = new StringBuilder();
if (data.getGroup() != null && !data.getGroup().trim().equals("")) {
dataBuilder.append(data.getGroup().trim());
// RFC says: Content lines SHOULD be folded to a maximum width of 75
// octets -> since it is SHOULD, we will just cut it as 74/75 chars
// depending if the last one fits in one char (note: chars != octet)
- boolean continuation = false;
- while (dataBuilder.length() > 0) {
- int stop = 74;
- if (continuation)
- stop--; // the space takes 1
- if (dataBuilder.length() > stop) {
- char car = dataBuilder.charAt(stop - 1);
+ int previous = 0;
+ for (int index = 0; index < dataBuilder.length(); previous = index) {
+ index += 74;
+ if (previous > 0)
+ index--; // the space takes 1
+ if (dataBuilder.length() > index) {
+ char car = dataBuilder.charAt(index - 1);
// RFC forbids cutting a character in 2
if (Character.isHighSurrogate(car)) {
- stop++;
+ index++;
}
}
- stop = Math.min(stop, dataBuilder.length());
- if (continuation) {
- lines.add(' ' + dataBuilder.substring(0, stop));
- } else {
- lines.add(dataBuilder.substring(0, stop));
- }
- dataBuilder.delete(0, stop);
-
- continuation = true;
+ index = Math.min(index, dataBuilder.length());
+ if (previous > 0)
+ writer.append(' ');
+ writer.append(dataBuilder, previous, index);
+ writer.append("\r\n");
}
-
- return lines;
}
/**
* @return the clone {@link Contact}
*/
public static Card clone(Card c) {
- return new Card(parseContact(toStrings(c)));
+ try {
+ File tmp = File.createTempFile("clone", ".vcf");
+ c.saveAs(tmp, Format.VCard21);
+
+ Card clone = new Card(tmp, Format.VCard21);
+ clone.unlink();
+ tmp.delete();
+
+ return clone;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
}
/**
* @return the clone {@link Contact}
*/
public static Contact clone(Contact c) {
- return parseContact(toStrings(c, -1)).get(0);
+ try {
+ File tmp = File.createTempFile("clone", ".vcf");
+ FileWriter writer = new FileWriter(tmp);
+ write(writer, c, -1);
+ writer.close();
+
+ Card clone = new Card(tmp, Format.VCard21);
+ clone.unlink();
+ tmp.delete();
+
+ return clone.remove(0);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
}
/**
import be.nikiroo.jvcard.Data;
import be.nikiroo.jvcard.parsers.Format;
import be.nikiroo.jvcard.parsers.Vcard21Parser;
+import be.nikiroo.jvcard.remote.SimpleSocket.BlockAppendable;
import be.nikiroo.jvcard.resources.StringUtils;
import be.nikiroo.jvcard.resources.bundles.RemoteBundle;
import be.nikiroo.jvcard.resources.enums.RemotingOption;
switch (command) {
case GET_CARD: {
- s.sendBlock(doGetCard(name));
+ sendCardBlock(s, name);
break;
}
case POST_CARD: {
switch (command) {
case GET_CONTACT: {
Contact contact = card.getById(cmd.getParam());
- if (contact != null)
- s.sendBlock(Vcard21Parser.toStrings(contact, -1));
- else
+ if (contact != null) {
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, contact, -1);
+ app.close();
+ } else {
s.sendBlock();
+ }
break;
}
case POST_CONTACT: {
case GET_DATA: {
for (Data data : contact) {
if (data.getName().equals(cmd.getParam())) {
- for (String line : Vcard21Parser.toStrings(data)) {
- s.send(line);
- }
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, data);
+ // note: we do NOT close 'app', since it would send an EOB
}
}
s.sendBlock();
* @throws IOException
* in case of error
*/
- private List<String> doGetCard(String name) throws IOException {
- List<String> lines = new LinkedList<String>();
-
+ private void sendCardBlock(SimpleSocket s, String name) throws IOException {
File vcf = getFile(name);
+ BlockAppendable app = s.createBlockAppendable();
if (vcf != null && vcf.exists()) {
Card card = new Card(vcf, Format.VCard21);
// timestamp + data
- lines.add(StringUtils.fromTime(card.getLastModified()));
- lines.addAll(Vcard21Parser.toStrings(card));
+ app.append(StringUtils.fromTime(card.getLastModified()) + "\r\n");
+ Vcard21Parser.write(app, card);
}
- return lines;
+ app.close();
}
/**
package be.nikiroo.jvcard.remote;
import java.io.BufferedReader;
+import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
* @author niki
*/
public class SimpleSocket {
+ /**
+ * An {@link Appendable} that can be used to send data over a
+ * {@link SimpleSocket}. You must close it to send the end of block element.
+ *
+ * @author niki
+ *
+ */
+ public class BlockAppendable implements Appendable, Closeable {
+ private SimpleSocket ss;
+
+ /**
+ * Create a new {@link BlockAppendable} for the given
+ * {@link SimpleSocket}.
+ *
+ * @param ss
+ * the {@link SimpleSocket}
+ */
+ public BlockAppendable(SimpleSocket ss) {
+ this.ss = ss;
+ }
+
+ @Override
+ public Appendable append(CharSequence csq) throws IOException {
+ ss.send(csq);
+ return this;
+ }
+
+ @Override
+ public Appendable append(char c) throws IOException {
+ ss.send("" + c);
+ return this;
+ }
+
+ @Override
+ public Appendable append(CharSequence csq, int start, int end)
+ throws IOException {
+ ss.send(csq.subSequence(start, end));
+ return this;
+ }
+
+ @Override
+ public void close() throws IOException {
+ ss.sendBlock();
+ }
+
+ }
+
/**
* The current version of the network protocol.
*/
* @throws IOException
* in case of IO error
*/
- protected void send(String data) throws IOException {
+ protected void send(CharSequence data) throws IOException {
if (data != null) {
- out.write(data);
+ out.append(data);
}
- out.write("\n");
+ out.append("\n");
if (out.checkError())
throw new IOException();
* in case of IO error
*/
public void sendCommand(Command command, String param) throws IOException {
- sendLine(new CommandInstance(command, param, CURRENT_VERSION).toString());
+ sendLine(new CommandInstance(command, param, CURRENT_VERSION)
+ .toString());
+ }
+
+ /**
+ * Create a new {@link Appendable} that can be used to send data on this
+ * {@link SimpleSocket}. When you are done, just call
+ * {@link BlockAppendable#close()}.
+ *
+ * @return the {@link Appendable}
+ */
+ public BlockAppendable createBlockAppendable() {
+ return new BlockAppendable(this);
}
/**
import be.nikiroo.jvcard.launcher.CardResult.MergeCallback;
import be.nikiroo.jvcard.parsers.Format;
import be.nikiroo.jvcard.parsers.Vcard21Parser;
+import be.nikiroo.jvcard.remote.SimpleSocket.BlockAppendable;
import be.nikiroo.jvcard.resources.StringUtils;
import be.nikiroo.jvcard.resources.bundles.RemoteBundle;
import be.nikiroo.jvcard.resources.enums.RemotingOption;
}
case POST_CARD: {
s.sendCommand(Command.POST_CARD);
- s.sendBlock(Vcard21Parser.toStrings(local));
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, local);
+ app.close();
local.saveAs(getCache(cacheDirOrig), Format.VCard21);
setLastModified(s.receiveLine());
break;
// ...but without starting with original since it is not
// true here
s.sendCommand(Command.POST_CARD);
- s.sendBlock(Vcard21Parser.toStrings(merge));
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, merge);
+ app.close();
String serverLastModifTime = s.receiveLine();
//
}
for (Contact c : added) {
s.sendCommand(Command.POST_CONTACT, c.getId());
- s.sendBlock(Vcard21Parser.toStrings(c, -1));
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, c, -1);
+ s.close();
}
if (from.size() > 0) {
for (int index = 0; index < from.size(); index++) {
}
for (Data d : subadded) {
s.sendCommand(Command.POST_DATA, d.getContentState(true));
- s.sendBlock(Vcard21Parser.toStrings(d));
+ BlockAppendable app = s.createBlockAppendable();
+ Vcard21Parser.write(app, d);
+ app.close();
}
s.sendCommand(Command.PUT_CONTACT);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
- ImageIO.write(image, "png", out);
+ ImageIO.write(image, "jpeg", out);
byte[] imageBytes = out.toByteArray();
imageString = DatatypeConverter.printBase64Binary(imageBytes);
kPane.addComponent(UiColors.createLabel(ColorOption.ACTION_KEY,
keyTrans));
- kPane.addComponent(UiColors.createLabel(ColorOption.ACTION_DESC, trans));
+ kPane.addComponent(UiColors.createLabel(ColorOption.ACTION_DESC,
+ trans));
actionPanel.addComponent(kPane);
}
}
if (width > 0) {
- actionPanel.addComponent(UiColors.createLabel(ColorOption.ACTION_DESC,
- StringUtils.padString("", width)));
+ actionPanel.addComponent(UiColors.createLabel(
+ ColorOption.ACTION_DESC, StringUtils.padString("", width)));
}
}
setMessage(mess, action.isError());
}
- if (action.onAction()) {
+ if (!action.isError() && action.onAction()) {
handleAction(action, null);
}
StringId.KEY_ACTION_EDIT_FIELD) {
@Override
public Object getObject() {
- return getSelectedData();
+ Data data = getSelectedData();
+ if (data != null && data.getB64Key() != -1) {
+ setMessage("Cannot modify binary values in RAW mode", true);
+ data = null;
+ }
+
+ return data;
}
@Override
@Override
protected List<TextPart> getLabel(int index, int width, boolean selected,
boolean focused) {
+
// TODO: from ini file?
int SIZE_COL_1 = 15;
int SIZE_COL_2_OPT = 10;
StringBuilder valueBuilder = new StringBuilder(" ");
if (!extMode) {
- valueBuilder.append(data.getValue().replaceAll("\n", "\\\\n"));
+ if (data.getB64Key() != -1) {
+ // TODO: i18n
+ valueBuilder.append("<BKey " + data.getB64Key() + ">");
+ } else {
+ valueBuilder.append(data.getValue().replaceAll("\n", "\\\\n"));
+ }
if (data.getGroup() != null && data.getGroup().length() > 0) {
valueBuilder.append("(");
valueBuilder.append(data.getGroup());
String[] array = contact.toStringArray(format, getSeparator(), " ",
width, Main.isUnicode());
-
+
if (contact.isDirty()) {
parts.add(new TextPart(" ", el));
parts.add(new TextPart("*", elDirty));