/*******************************************************************************
blogger-cli Консольные инструменты по работе с blogger.com
(с) Камнев Георгий Павлович 2011 GPLv2
Данная программа является свободным программным обеспечением. Вы вправе
распространять ее и/или модифицировать в соответствии с условиями версии 2
либо по вашему выбору с условиями более поздней версии
Стандартной Общественной Лицензии GNU, опубликованной Free Software Foundation.
Мы распространяем данную программу в надежде на то, что она будет вам полезной,
однако НЕ ПРЕДОСТАВЛЯЕМ НА НЕЕ НИКАКИХ ГАРАНТИЙ,
в том числе ГАРАНТИИ ТОВАРНОГО СОСТОЯНИЯ ПРИ ПРОДАЖЕ
и ПРИГОДНОСТИ ДЛЯ ИСПОЛЬЗОВАНИЯ В КОНКРЕТНЫХ ЦЕЛЯХ.
Для получения более подробной информации ознакомьтесь
со Стандартной Общественной Лицензией GNU.
Вместе с данной программой вы должны были получить экземпляр
Стандартной Общественной Лицензии GNU.
Если вы его не получили, сообщите об этом в Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*******************************************************************************/
package tv.cofe.blogger;
import com.google.gdata.client.Query;
import com.google.gdata.client.blogger.BloggerService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Entry;
import com.google.gdata.data.Feed;
import com.google.gdata.data.HtmlTextConstruct;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.gocha.collection.Convertor;
import org.gocha.collection.Iterators;
import org.gocha.files.FileUtil;
//import org.gocha.jdk.JaninoCompiler;
import org.gocha.jdk.LocalMessagesImplementor;
import org.gocha.text.IndentStackWriter;
import org.gocha.text.TextUtil;
/**
* Точка входа
* @author gocha
*/
public class CLI
{
private Map<String,String> execArgs = null;
public CLI(){
execArgs = new HashMap<String, String>();
}
public CLI(Map<String,String> execArgs, File appDir){
if (execArgs== null) {
throw new IllegalArgumentException("execArgs==null");
}
if (appDir== null) {
throw new IllegalArgumentException("appDir==null");
}
this.execArgs = execArgs;
this.appDir = appDir;
}
// <editor-fold defaultstate="collapsed" desc="Сообщения messages">
private static Messages messages = null;
public static Messages messages(){
return messages;
}
private static void initMessages(File appd){
if( messages!=null )return;
if (appd== null) {
throw new IllegalArgumentException("appd==null");
}
LocalMessagesImplementor lmi = new LocalMessagesImplementor();
lmi.setCompiler(new org.gocha.jdk.JaninoCompiler());
lmi.setClassName(Messages.class.getName()+"Impl");
lmi.setInterface(Messages.class);
lmi.setClassRootDirectory(new File(new File(appd,"cache"),"classes"));
lmi.setMessageDirectory(new File(appd,"locale"));
// Устанавливаются по умолчанию
// lmi.setCacheByteCode(true);
// lmi.setFileFormat(LocalMessagesImplementor.FileFormat.xml);
// lmi.setMessageFilePrefix("messages");
Object o = lmi.getInstance();
if( o instanceof Messages ){
messages = (Messages)o;
return;
}
throw new Error( "failed init messages" );
}
// </editor-fold>
public static String METAFEED_URL = "http://www.blogger.com/feeds/default/blogs";
public static String FEED_URI_BASE = "http://www.blogger.com/feeds";
public static String POSTS_FEED_URI_SUFFIX = "/posts/default";
public static String COMMENTS_FEED_URI_SUFFIX = "/comments/default";
// <editor-fold defaultstate="collapsed" desc="eval() - интерпретация аргументов">
@Retention(RetentionPolicy.RUNTIME)
public @interface Do
{
String desc() default "";
}
public static class MenuItem
{
protected Do info;
protected Method method;
protected Object owner;
public Do getInfo() {
return info;
}
public void setInfo(Do info) {
this.info = info;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Object getOwner() {
return owner;
}
public void setOwner(Object owner) {
this.owner = owner;
}
public void eval() {
if (method == null) return;
try {
method.invoke(owner);
} catch (IllegalAccessException ex) {
throw new Error(ex.getMessage(), ex);
} catch (IllegalArgumentException ex) {
throw new Error(ex.getMessage(), ex);
} catch (InvocationTargetException ex) {
throw new Error(ex.getMessage(), ex);
}
}
public static MenuItem[] find(Object owner) {
if (owner == null) {
throw new IllegalArgumentException("owner==null");
}
Class clazz = owner.getClass();
Method[] methods = clazz.getMethods();
ArrayList<MenuItem> l = new ArrayList<MenuItem>();
for (Method m : methods) {
Do d = m.getAnnotation(Do.class);
if (d == null)
continue;
if (m.getParameterTypes().length == 0) {
MenuItem mi = new MenuItem();
mi.setInfo(d);
mi.setMethod(m);
mi.setOwner(owner);
l.add(mi);
}
}
return l.toArray(new MenuItem[]{});
}
}
public void eval() {
MenuItem[] _mi = MenuItem.find(this);
HashMap<String,String> v = new HashMap<String, String>();
HashMap<String,String> doDesc = new HashMap<String, String>();
for( MenuItem mi : _mi ){
String message = null;
try{
String desc = mi.getInfo().desc();
if( desc!=null && desc.length()>0 ){
Object res = messages().getClass().getMethod(desc).invoke(messages());
if( res!=null && res instanceof String )message = (String)res;
}
}
catch(Throwable ex){
System.out.println("err "+ex);
}
if( message==null || message.length()==0 )message = mi.getInfo().desc();
doDesc.put(mi.getMethod().getName(), message);
}
if( !isArgumentExists("do", true) ){
v.put("do", messages()._do());
v.put("desc", messages().desc());
TextUtil.printTemplate(System.out, "{do:>15} {desc:60}", v);
for( String k : doDesc.keySet() ){
v.put("do", k );
v.put("desc", doDesc.get(k));
TextUtil.printTemplate(System.out, "{do:>15} {desc:60}", v);
}
}
String d = getDo();
if (d == null)return;
boolean found = false;
for( MenuItem mi : _mi ){
Method m = mi.getMethod();
String methodName = m.getName();
if( d.equals(methodName) ){
if( doDesc.containsKey(d) )
System.out.println(doDesc.get(d)+":");
mi.eval();
found = true;
}
}
if( !found ){
System.out.println(messages().notValidDoArgument());
}
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Чтение аргументов">
// <editor-fold defaultstate="collapsed" desc="storage">
private Properties store = null;
private String readStoredArg(String name, String def) {
if (store == null)
store = readStore();
if (store != null && store.containsKey(name))
return store.getProperty(name, def);
return def;
}
private void writeStoredArg(String name, String value) {
if (store == null)
store = readStore();
if (store == null)
store = new Properties();
store.put(name, value);
writeStore(store);
}
private void removeStoredArg(String name) {
if (store == null)
store = readStore();
if (store == null)
store = new Properties();
store.remove(name);
writeStore(store);
}
private void writeStore(Properties p) {
File args = getStoreFile();
if (!args.getParentFile().exists()) {
args.getParentFile().mkdirs();
}
try {
FileUtil.writeProperties(args, p);
} catch (IOException ex) {
Logger.getLogger(CLI.class.getName()).log(Level.WARNING, null, ex);
}
}
private Properties readStore() {
File args = getStoreFile();
if (args.exists() && args.isFile() && args.canRead()) {
try {
Properties _store = FileUtil.readProperties(args);
return _store;
} catch (IOException ex) {
Logger.getLogger(CLI.class.getName()).log(Level.WARNING, null, ex);
}
}
return null;
}
private File getStoreFile() {
return new File(getAppDir(), "args.xml");
}// </editor-fold>
private Boolean overwriteAllArgs = null;
public boolean isArgumentExists(String name,boolean inStore){
if (execArgs.containsKey(name)) return true;
String stored = readStoredArg(name, null);
return inStore ? stored !=null : false;
}
public String readArgument(String name, String defValue, boolean isPassword, boolean allowOtherArgs, Boolean _learn) {
Scanner in = System.console()==null ?
new Scanner( System.in ) :
new Scanner( System.console().reader() );
PrintWriter out =
System.console()==null ?
new PrintWriter(System.out):
System.console().writer();
if (execArgs.containsKey(name)) return execArgs.get(name);
boolean learn = _learn==null ?
(allowOtherArgs && getArgMode()==ArgumentMode.Learning) :
(boolean)_learn;
String stored = readStoredArg(name, null);
if( stored!=null ){
execArgs.put(name, stored);
if( isVerbose() )out.println(messages().readedArgumentFromFile(name, getStoreFile()));
out.flush();
if( overwriteAllArgs!=null && !overwriteAllArgs ){
return stored;
}
if( learn && ( overwriteAllArgs==null ) ){
out.println(messages().redefineArgument(name));
out.print("> ");
out.flush();
String overWrite = in.nextLine();
if( overWrite.equals("n") )return stored;
if( overWrite.equals("Y") ){
overwriteAllArgs = true;
}
if( overWrite.equals("N") ){
overwriteAllArgs = false;
return stored;
}
}else{
return stored;
}
}
if (allowOtherArgs && isReadStdInArg()){
String eol = System.getProperty("line.separator");
if( eol==null )eol = "\n";
String line = null;
out.println(messages().enterArgument(name));
if( defValue!=null )out.println(messages().defaultArgumentValue(defValue));
while( true ){
out.print("> ");
out.flush();
String l = null;
if( System.console()!=null ){
l = isPassword ? new String(System.console().readPassword()) : in.nextLine();
}else{
l = in.nextLine();
}
if( l.endsWith("\\") && !isPassword ){
if( line==null )line = TextUtil.trimEnd(l, "\\");
else line = line + eol + TextUtil.trimEnd(l, "\\");
continue;
}else{
if( line==null )line = l;
else line = line + eol + l;
}
break;
}
if( learn && line!=null ){
out.println(messages().saveArgumentValue());
out.print( "> " );
out.flush();
String save = in.nextLine();
if( save!=null ){
if( save.trim().toLowerCase().equals("y") ){
writeStoredArg(name, line);
}else if( save.trim().toLowerCase().equals("n") ){
removeStoredArg(name);
}
}
}
execArgs.put(name, line);
return line;
}
return defValue;
}
public String argument(String name, String defValue) {
return readArgument(name, defValue, false, true, null);
}
public String argument(String name, String defValue,boolean allowOtherArgs ) {
return readArgument(name, defValue, false, allowOtherArgs, null);
}
public boolean argument(String name, boolean defValue) {
String a = argument(name, null);
if( a==null )return defValue;
String val = a;
if (val == null)return defValue;
if (val.equalsIgnoreCase("true"))return true;
if (val.equalsIgnoreCase("yes"))return true;
if (val.equalsIgnoreCase("on"))return true;
if (val.equalsIgnoreCase("false"))return false;
if (val.equalsIgnoreCase("no"))return false;
if (val.equalsIgnoreCase("off"))return false;
return defValue;
}
public boolean argument(String name, boolean defValue,boolean allowOtherArgs) {
String a = readArgument(name, null, false, allowOtherArgs, null);
if( a==null )return defValue;
String val = a;
if (val == null)return defValue;
if (val.equalsIgnoreCase("true"))return true;
if (val.equalsIgnoreCase("yes"))return true;
if (val.equalsIgnoreCase("on"))return true;
if (val.equalsIgnoreCase("false"))return false;
if (val.equalsIgnoreCase("no"))return false;
if (val.equalsIgnoreCase("off"))return false;
return defValue;
}
// public long argumentTime(String name,long defaultTimeStamp,boolean allowOtherArgs){
// String v = argument(name, Long.toString(defaultTimeStamp), allowOtherArgs);
// }
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргументы">
// <editor-fold defaultstate="collapsed" desc="Аргумент: appDir">
private File appDir = null;
public File getAppDir() {
if (appDir != null)
return appDir;
String def = System.getProperty("user.home", null);
if (def != null) {
String fileSeparator = System.getProperty("file.separator", "/");
def = TextUtil.trimEnd(def, fileSeparator) + "/.tv/cofe/blogger".replace("/", fileSeparator);
}else{
throw new Error("System.getProperty() == null");
}
if (execArgs.containsKey("appDir")) {
appDir = new File(execArgs.get("appDir"));
} else {
appDir = new File(def);
}
return appDir;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: ArgumentMode">
public static enum ArgumentMode
{
Normal,
Learning
}
private ArgumentMode mode = null;
public ArgumentMode getArgMode() {
if (mode != null)
return mode;
String m = readArgument("argumentMode", null, false, false, null);
if (m == null)
m = readStoredArg("argumentMode", null);
if (m != null && m.equalsIgnoreCase("normal")) {
mode = ArgumentMode.Normal;
} else if (m != null && m.toLowerCase().startsWith("learn")) {
mode = ArgumentMode.Learning;
} else {
mode = ArgumentMode.Normal;
}
return mode;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: do">
private String _do = null;
public String getDo() {
if (_do != null)
return _do;
_do = readArgument("do", null, false, true, false);
return _do;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: verbose">
private Boolean verbose = null;
public Boolean isVerbose() {
if (verbose != null)
return verbose;
String v = readArgument("verbose", "false", false, false, false);
if( v.equalsIgnoreCase("true") )verbose = true;
else if( v.equalsIgnoreCase("on") )verbose = true;
else if( v.equalsIgnoreCase("1") )verbose = true;
else if( v.equalsIgnoreCase("false") )verbose = false;
else if( v.equalsIgnoreCase("off") )verbose = false;
else if( v.equalsIgnoreCase("0") )verbose = false;
else verbose = false;
return verbose;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: feedSplitter">
private String feedSplitter = null;
public String getFeedSplitter() {
if (feedSplitter != null)return feedSplitter;
feedSplitter = argument("feedSplitter", "\n");
return feedSplitter;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: readStdInArg">
private Boolean readStdInArg = null;
/**
* Аргумент readStdInArg: true - Допускается чтение из SIDIO/Консольни недостающих параметров
* @return true - Допускается чтение из SIDIO/Консольни
*/
public Boolean isReadStdInArg() {
if (readStdInArg != null)
return readStdInArg;
if (execArgs.containsKey("readStdInArg")) {
String val = execArgs.get("readStdInArg");
if (val.equalsIgnoreCase("true"))
readStdInArg = true;
else if (val.equalsIgnoreCase("yes"))
readStdInArg = true;
else if (val.equalsIgnoreCase("on"))
readStdInArg = true;
else if (val.equalsIgnoreCase("false"))
readStdInArg = false;
else if (val.equalsIgnoreCase("no"))
readStdInArg = false;
else if (val.equalsIgnoreCase("off"))
readStdInArg = false;
else
readStdInArg = true;
} else {
readStdInArg = true;
}
return readStdInArg;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: gmailLogin">
private String gmailLogin = null;
public String getGmailLogin() {
if (gmailLogin != null)
return gmailLogin;
gmailLogin = argument("gmailLogin", null);
return gmailLogin;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: gmailPassword">
private String gmailPassword = null;
public String getGmailPassword() {
if (gmailPassword != null)
return gmailPassword;
gmailPassword = argument("gmailPassword", null);
return gmailPassword;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: appName">
private String appName = null;
public String getAppName() {
if (appName != null)
return appName;
appName = argument("appName", "blogger-tools");
return appName;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: blogID">
private String blogID = null;
public String getBlogID() {
if (blogID != null)
return blogID;
blogID = argument("blogID", null);
return blogID;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: feedTmpl">
private String feedTmpl = null;
public String getFeedTmpl() {
if (feedTmpl != null)
return feedTmpl;
feedTmpl = argument("feedTmpl", "{_vars}");
return feedTmpl;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: exportFileTmpl">
private String exportFileTmpl = null;
public String getExportFileTmpl() {
if (exportFileTmpl != null)
return exportFileTmpl;
exportFileTmpl = argument("exportFileTmpl", "./{id}.html");
return exportFileTmpl;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: exportFile">
private String exportFile = null;
public String getExportFile() {
if (exportFile != null)
return exportFile;
exportFile = argument("exportFile", "./exported.html");
return exportFile;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: limitEntries">
private Integer limitEntries = null;
public int getLimitEntries() {
if (limitEntries != null)
return limitEntries;
String v = argument("limitEntries", "25");
if (v == null)
throw new Error("limitEntries=null");
try {
int r = Integer.parseInt(v);
if (r < 0)
throw new Error("limitEntries<0");
limitEntries = r;
} catch (NumberFormatException ex) {
throw new Error(ex.getMessage(), ex);
}
return limitEntries;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: postID">
private String postID = null;
public String getPostID() {
if (postID != null)
return postID;
postID = argument("postID", null);
return postID;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: htmlMetaTmpl">
private String htmlMetaTmpl = null;
public String getHtmlMetaTmpl() {
if (htmlMetaTmpl != null)
return htmlMetaTmpl;
htmlMetaTmpl = argument("htmlMetaTmpl", "<meta name=\"{key}\" content=\"{value}\" />");
return htmlMetaTmpl;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: htmlTmpl">
private String htmlTmpl = null;
public String getHtmlTmpl() {
if (htmlTmpl != null)
return htmlTmpl;
htmlTmpl = argument("htmlTmpl",
"<!DOCTYPE html>"
+ "<html>"
+ "<head>"
+ "<title>{title}</title>"
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset={charset}\"/>"
+ "{htmlMeta}"
+ "</head>"
+ "<body>"
+ "{htmlBody}"
+ "</body>"
+ "</html>");
return htmlTmpl;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: dateFormat">
private String dateFormatPattern = null;
public String getDateFormat() {
if (dateFormatPattern != null)return dateFormatPattern;
dateFormatPattern = argument("dateFormat","yyyy-MM-dd HH:mm",false);
return dateFormatPattern;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: addPostFile">
private String addPostFile = null;
public String getAddPostFile() {
if (addPostFile != null)return addPostFile;
addPostFile = argument("addPostFile",null);
return addPostFile;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: postTitle">
private String postTitle = null;
public String getPostTitle() {
if (postTitle != null)return postTitle;
postTitle = argument("postTitle",null,false);
return postTitle;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: draftPost">
private Boolean draftPost = null;
public boolean isDraftPost() {
if (draftPost != null)return draftPost;
draftPost = argument("draftPost",false);
return draftPost;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: timeZone">
private java.util.TimeZone timeZone = null;
public java.util.TimeZone getTimeZone() {
if (timeZone != null)return timeZone;
String sTimeZone = argument("timeZone","default");
if( sTimeZone==null ){
timeZone = java.util.TimeZone.getDefault();
}else{
if( sTimeZone.equalsIgnoreCase("default") ){
timeZone = java.util.TimeZone.getDefault();
}else{
timeZone = java.util.TimeZone.getTimeZone(sTimeZone);
}
}
return timeZone;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: timeShift">
private Long timeShift = null;
public Long getTimeShift() {
if (timeShift != null)return timeShift;
String v = argument("timeShift","00:00");
if( v!=null ){
try{
long ts = TextUtil.stringToTimeStamp(v);
timeShift = ts;
}catch(Throwable e){
timeShift = 0L;
}
}
return timeShift;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: publishedDate">
private Date publishedDate = null;
public Date getPublishedDate() {
if (publishedDate != null)return publishedDate;
String v = argument("publishedDate",null, false);
if( v!=null ){
try{
if( v.equalsIgnoreCase("now") ){
publishedDate = new Date();
}else{
Date d = dateParser().parse(v);
publishedDate = d;
}
}catch(Throwable e){
publishedDate = null;
}
}
return publishedDate;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: editedDate">
private Date editedDate = null;
public Date getEditedDate() {
if (editedDate != null)return editedDate;
String v = argument("editedDate",null, false);
if( v!=null ){
try{
if( v.equalsIgnoreCase("now") ){
editedDate = new Date();
}else{
Date d = dateParser().parse(v);
editedDate = d;
}
}catch(Throwable e){
editedDate = null;
}
}
return editedDate;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Аргумент: updatedDate">
private Date updatedDate = null;
public Date getUpdatedDate() {
if (updatedDate != null)return updatedDate;
String v = argument("updatedDate",null, false);
if( v!=null ){
try{
if( v.equalsIgnoreCase("now") ){
updatedDate = new Date();
}else{
Date d = dateParser().parse(v);
updatedDate = d;
}
}catch(Throwable e){
updatedDate = null;
}
}
return updatedDate;
}// </editor-fold>
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="BloggerService">
private BloggerService service = null;
public BloggerService getService() {
if (service != null)return service;
String login = getGmailLogin();
String psswd = getGmailPassword();
if (login == null)throw new ArgumentNotSetException("gmailLogin");
if (psswd == null)throw new ArgumentNotSetException("gmailPassword");
service = new BloggerService(getAppName());
try {
service.setUserCredentials(login, psswd);
} catch (AuthenticationException ex) {
throw new Error(ex.getMessage(),ex);
}
return service;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="id convertors">
protected static String getBlogID(Entry e) {
return e.getId().split("blog-")[1];
}
public static final Convertor<Entry, String> blogIDConvertor =
new Convertor<Entry, String>()
{
@Override
public String convert(Entry from) {
return getBlogID(from);
}
};
protected static String getPostID(Entry e) {
return e.getId().split("post-")[1];
}
public static final Convertor<Entry, String> postIDConvertor =
new Convertor<Entry, String>()
{
@Override
public String convert(Entry from) {
return getPostID(from);
}
};
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="println feed">
public void println(Feed feed,Convertor<Entry,String> idConvert) {
String feedSplttr = getFeedSplitter();
if (feed.getEntries().size() > 0) {
HashMap<String, String> v = new HashMap<String, String>();
int totCo = feed.getTotalResults();
boolean totCoUndef = totCo == Query.UNDEFINED;
int co = 0;
int maxCo = totCoUndef ? getLimitEntries() : totCo;
for (Entry e : new EntryIterable(feed)) {
if( e==null )continue;
if( co>0 )System.out.print(feedSplttr);
v.putAll( PostHelper.getFlatProperties(e,idConvert,dateTimeConvertor) );
v.put("_vars",
TextUtil.join(
Iterators.toArray(v.keySet(), new String[]{}),
",")
);
if( co==0 ){
HashMap<String,String> vt = new HashMap<String, String>();
for(String k:v.keySet())vt.put(k, k);
TextUtil.printTemplate(System.out, getFeedTmpl(), vt);
}
TextUtil.printTemplate(System.out, getFeedTmpl(), v);
co++;
if (co >= maxCo)break;
}
if( isVerbose() )System.out.println(messages().entriesCount(co));
} else {
System.out.println(messages().entriesNotExists());
}
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="getPostsFeed()">
/**
* Получение фида постов
* @return фид постов
* @throws ServiceException
* @throws IOException
*/
public Feed getPostsFeed() throws ServiceException, IOException {
URL feedUrl = new URL(FEED_URI_BASE + "/" + getBlogID() + "/posts/default");
Feed feed = getService().getFeed(feedUrl, Feed.class);
return feed;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="getPostByID()">
/**
* Получение поста по ID
* @param postID ID поста
* @return Пост
* @throws ServiceException Ошибка сервиса...
* @throws IOException IO ошибка
*/
public Entry getPostByID(String postID) throws ServiceException, IOException {
if (postID == null) {
throw new IllegalArgumentException("postID==null");
}
URL url = new URL(FEED_URI_BASE + "/" + getBlogID() + "/posts/default/" + postID);
return getService().getEntry(url, Entry.class);
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="clearPasswords()">
@Do(desc = "tipDoClearPasswords")
public void clearPasswords() {
removeStoredArg("gmailPassword");
removeStoredArg("gmailLogin");
}// </editor-fold>
protected Convertor<DateTime,String> dateTimeConvertor = new Convertor<DateTime, String>() {
@Override
public String convert(DateTime from) {
long shift = getTimeShift();
Date date = new Date(from.getValue()-shift);
return dateParser().format(date);
}
};
// <editor-fold defaultstate="collapsed" desc="Обзор и экспорт постов">
@Do(desc = "tipDoLookupBlogs")
public void lookupBlogs() {
try {
URL feedUrl = new URL(METAFEED_URL);
Feed feed = getService().getFeed(feedUrl, Feed.class);
println(feed, blogIDConvertor);
} catch (IOException ex) {
throw new Error(ex.getMessage(), ex);
} catch (ServiceException ex) {
throw new Error(ex.getMessage(), ex);
}
}
@Do(desc = "tipDoLookupPosts")
public void lookupPosts() {
try {
println(getPostsFeed(), postIDConvertor);
} catch (IOException ex) {
throw new Error(ex.getMessage(), ex);
} catch (ServiceException ex) {
throw new Error(ex.getMessage(), ex);
}
}
public void exportPost(Entry e) {
if (e == null) {
throw new IllegalArgumentException("e==null");
}
String fileTmpl = getExportFileTmpl();
if (fileTmpl == null)
throw new ArgumentNotSetException("exportFileTmpl");
Map<String, String> props = PostHelper.getFlatProperties(e, postIDConvertor,dateTimeConvertor);
File f = new File(TextUtil.template(fileTmpl, props));
File fdir = f.getParentFile();
if( fdir!=null ){
if( !fdir.exists() ){
fdir.mkdirs();
}
}
PostHelper.export(f, e, getHtmlTmpl(), getHtmlMetaTmpl(),dateTimeConvertor);
System.out.println(messages().postExportedTo(props.get("title"), f));
}
@Do(desc = "tipDoExportPosts")
public void exportPosts() {
try {
Feed f = getPostsFeed();
int co = 0;
int tot = f.getTotalResults();
boolean totCoUndef = tot == Query.UNDEFINED;
int max = totCoUndef ? getLimitEntries() : tot;
for (Entry e : new EntryIterable(f)) {
co++;
if (co > max)
break;
exportPost(e);
}
} catch (IOException ex) {
throw new Error(ex.getMessage(), ex);
} catch (ServiceException ex) {
throw new Error(ex.getMessage(), ex);
}
}
@Do(desc = "tipDoExportPost")
public void exportPost() {
try {
String postId = getPostID();
Entry e = getPostByID(postId);
if (e != null) {
exportPost(e);
}
} catch (IOException ex) {
throw new Error(ex.getMessage(), ex);
} catch (ServiceException ex) {
throw new Error(ex.getMessage(), ex);
}
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="dateFormat">
private SimpleDateFormat dateParser = null;
/**
* Парсер даты
* @return Парсер даты
*/
private SimpleDateFormat dateParser() {
if (dateParser != null)
return dateParser;
String ptrn = getDateFormat();
if( ptrn==null )ptrn = "yyyy-MM-dd HH:mm";
dateParser = new SimpleDateFormat(ptrn);
return dateParser;
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Добавить пост, очиска html">
@Do(desc = "tipDoAddPost")
public void addPost() {
try {
String fileName = getAddPostFile();
if (fileName == null) {
throw new ArgumentNotSetException("addPostFile");
}
File file = new File(fileName);
HtmlFile htmlFile = HtmlFile.read(file);
String htmlPostContent = htmlFile.getBody();
if (htmlPostContent == null)
throw new Error(messages().htmlBodyNotFound(file));
String title = null;
if (title == null)
title = getPostTitle();
if (title == null)
title = htmlFile.getTitle();
if (title == null)
throw new ArgumentNotSetException("postTitle");
boolean isDraftPost = isDraftPost();
long postTimeShift = getTimeShift();
Date postDate = new Date(htmlFile.getLastModifiedDate().getTime() + postTimeShift);
Entry e = new Entry();
e.setTitle(new PlainTextConstruct(title));
e.setDraft(isDraftPost);
e.setContent(new HtmlTextConstruct(htmlFile.getBody()));
java.util.TimeZone tz = getTimeZone();
if (tz == null)
throw new ArgumentNotSetException("timeZone");
Date pubDate = getPublishedDate()==null ? postDate : getPublishedDate();
Date edtDate = getEditedDate()==null ? postDate : getEditedDate();
Date updDate = getUpdatedDate()==null ? postDate : getUpdatedDate();
e.setPublished(new DateTime(pubDate, tz));
e.setEdited(new DateTime(edtDate, tz));
e.setUpdated(new DateTime(updDate, tz));
String blogId = getBlogID();
if (blogId == null)
throw new ArgumentNotSetException("blogID");
BloggerService srvc = getService();
URL url = new URL(FEED_URI_BASE + "/" + blogId + POSTS_FEED_URI_SUFFIX);
srvc.insert(url, e);
} catch (IOException ex) {
throw new Error(ex.getMessage(), ex);
} catch (ServiceException ex) {
throw new Error(ex.getMessage(), ex);
}
}
@Do(desc = "tipDoCleanPost")
public void cleanPost() {
String fileName = getAddPostFile();
if (fileName == null) {
throw new ArgumentNotSetException("addPostFile");
}
File file = new File(fileName);
HtmlFile htmlFile = HtmlFile.read(file);
String htmlPostContent = htmlFile.getBody();
if (htmlPostContent == null)
throw new Error(messages().htmlBodyNotFound(file));
String title = null;
if (title == null)
title = getPostTitle();
if (title == null)
title = htmlFile.getTitle();
if (title == null)
throw new ArgumentNotSetException("postTitle");
String sExportFile = getExportFile();
if (sExportFile == null)
throw new ArgumentNotSetException("exportFile");
File _exportFile = new File(sExportFile);
Charset cs = FileUtil.UTF8();
Map<String, String> t = new HashMap<String, String>();
t.put("charset", cs.name());
String metaCharset = "<meta http-equiv=\"content-type\" content=\"text/html; charset={charset}\"/>";
metaCharset = TextUtil.template(metaCharset, t);
t.put("title", TextUtil.htmlEncode(title));
String headTitle = "<title>{title}</title>";
headTitle = TextUtil.template(headTitle, t);
t.put("metaCharset", metaCharset);
t.put("title", headTitle);
String head = "{metaCharset}\n{title}\n";
head = TextUtil.template(head, t);
t.put("head", head);
t.put("body", htmlPostContent);
String fileBody = "<html><head>{head}</head><body>{body}</body></html>";
fileBody = TextUtil.template(fileBody, t);
FileUtil.writeAllText(_exportFile, cs, fileBody);
}// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Входная точка приложения">
/**
* @param args Аргументы коммандной строки
*/
public static void main(String[] args) {
// Чтение параметров
Map<String, String> m = new HashMap<String, String>();
m.putAll(System.getenv());
for (int i = 0; i < args.length - 1; i++) {
if (args[i].startsWith("-") && args[i].length() > 0) {
m.put(args[i].substring(1), args[i + 1]);
}
}
// Анализ переданых параметров
boolean printException =
m.containsKey("printException") ?
(
m.get("printException").equalsIgnoreCase("true") ||
m.get("printException").equalsIgnoreCase("on")
)
: true;
boolean verbose =
m.containsKey("verbose") ?
(
m.get("verbose").equalsIgnoreCase("true") ||
m.get("verbose").equalsIgnoreCase("on")
)
: false;
boolean logException =
m.containsKey("logException") ?
(
m.get("logException").equalsIgnoreCase("true") ||
m.get("logException").equalsIgnoreCase("on")
)
: false;
boolean printExceptionStack =
m.containsKey("printExceptionStack") ?
(
m.get("printExceptionStack").equalsIgnoreCase("true") ||
m.get("printExceptionStack").equalsIgnoreCase("on")
)
: false;
try {
// Определяем расположение домашнего каталога программы
File appd = getAppDir(m);
// Инициализация сообщений
initMessages(appd);
String version = getVersion();
if( version==null )version="?";
String title = getTitle();
if( title==null )title = "?";
String vendor = getVendor();
if( vendor==null )vendor = "?";
if( verbose )
System.out.println(messages().logo(title, vendor, version));
CLI cli = new CLI(m,appd);
printException = cli.argument("printException", printException, false);
printExceptionStack = cli.argument("printExceptionStack", printException, false);
logException = cli.argument("logException", logException, false);
cli.eval();
} catch (Throwable ex) {
if( printException )print(ex,0,null,printExceptionStack);
if( logException )Logger.getLogger(CLI.class.getName()).log(Level.SEVERE, null, ex);
System.exit(1);
}
}
private static String getVersion(){
return CLI.class.getPackage().getImplementationVersion();
}
private static String getTitle(){
return CLI.class.getPackage().getImplementationTitle();
}
private static String getVendor(){
return CLI.class.getPackage().getImplementationVendor();
}
private static File getAppDir(Map<String, String> m){
File appd = m.containsKey("appDir") ? new File(m.get("appDir")) : null;
if( appd==null ){
String def = System.getProperty("user.home", null);
if (def != null) {
String fileSeparator = System.getProperty("file.separator", "/");
def = TextUtil.trimEnd(def, fileSeparator) + "/.tv/cofe/blogger".replace("/", fileSeparator);
}else{
throw new Error("System.getProperty(\"user.home\") == null");
}
appd = new File(def);
}
return appd;
}
private static void print(Throwable ex,int level,IndentStackWriter writer,boolean printExceptionStack){
if( level<0 )level = 0;
if( writer==null )writer = new IndentStackWriter(new OutputStreamWriter(System.out));
String message = ex.getMessage();
String clazz = ex.getClass().getName();
writer.level(level);
writer.indent(" ");
writer.print(clazz);
if( message!=null ){
writer.print(": ");
writer.print(message);
}
writer.println();
writer.flush();
Throwable cex = ex.getCause();
if( cex!=null ){
print( cex, level+1, writer, printExceptionStack );
}
if( cex==null && printExceptionStack ){
writer.level(level);
writer.println();
writer.println("Stack:");
ex.printStackTrace(writer);
writer.flush();
}
}
// </editor-fold>
}