2014年12月17日 星期三

iText pdf writer template

一般報表會用到pdf資訊,就表示是只能讀(不然報表用xls可以再做運算,是更好分析的)
所以會比較偏向,產出正式文件報告或是紙本列印的功能取向...
當然就少不了固定的格式,頁首頁尾,或是套表之類的~
這時候,當然就是用template囉~

iText在java界應該算是個有名的project...也不少年了~
而且還大改過版~不知道該高興或難過,資源太多~很容易找到過時的Orz...

所以版號很重要,使用的package要看清楚,使用的方式要看清楚,不然就會像我一樣一直被表一_一|||...

現在用的是新版5+...package在 com.itextpdf.text.xxxxxx..
主要用以產生pdf...先說手刻pdf,和使用template的方式,兩邊實作的寫法差很多...
雖然官方文件看起來寫得精美,但是因為一直看到手刻的作法不同,然後一直被誤導Orz..

要先說~基本常用的document.add(xxxx)在使用template後是無效的,所以一旦使用template,就得全都用template的解法處理同一頁面(也許是因為我的template本來就是一整頁了...動態資訊用套表的方式寫入)...而Font,BaseFont,基本上也只是對手刻有用~在PdfContentByte裡,只有部分有效...
Paragraph 和 PdfContentByte不同,請把PdfContentByte當成是canvas的Stoke來想,會合理一點Orz...

#基本的template import
import com.itextpdf.text.*;
import com.itextpdf.text.*;

// Create PDF
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, fos);
document.open();
PdfContentByte pcb = writer.getDirectContent();

// Load existing PDF(template)
PdfReader reader = new PdfReader(fis);
PdfImportedPage page = writer.getImportedPage(reader, 1);

// PDF new page
writer.setPageEmpty(true);
document.newPage();

// Add Template
pcb.addTemplate(page, 0, 0);

document.close();

#若在template裡面要加圖,其圖檔也要做成template才能匯入
//Read Image to template
Image img = Image.getInstance(img1);
img.scalePercent(imgscale);    //unit %
img.setAbsolutePosition(0, 0);    //must
PdfTemplate myimg1 = pcb.createTemplate(img.getWidth(), img.getHeight());
myimg1.addImage(img);

//Put Image into template position p(X,Y)
pcb.addTemplate(myimg1, p.getX(), p.getY());

#在template裡加文字(數字基本上就是自己轉好文字format加對齊方式丟進去)
#begin/EndText 是必須的,就跟open/closePath那種一樣~
#如果要換行時怎麼辦呢~基本上他是不認\n,所以,就自己算位置吧~囧/
pcb.beginText();     //must
pcb.setFontAndSize(bf, pis.getFontsize());
//number align right
//pcb.showTextAligned(Element.ALIGN_RIGHT, p.getValue(), p.getX(), p.getY(), 0);
//string align left
pcb.showTextAligned(Element.ALIGN_LEFT, p.getValue(), p.getX(), p.getY(), 0);
pcb.endText();    //must

#文字指定顏色,粗體(bold/strong)...這裡就很奸詐了,因為在這裡pcb調用的BaseFont,只有字型和字型大小有用。但是文字的顏色與特效就需要特別的方式來寫..去改BaseFont只會讓你一直翻桌而已一.一
#重點在於setTextRenderingMode要改,不過粗體字線要多粗,是自己設定的
#因為 PdfContentByte 運作的方式跟canvas的方式非常相似,所以顏色,線條,特效改過後,再下次使用要重設定,不然就會看到後面一整串字都一起紅通通了XD
//font color and bold
pcb.beginText();     //must
pcb.setFontAndSize(bf, pis.getFontsize());                           
pcb.setRGBColorFill(255, 0, 0); //for change color
pcb.setLineWidth(0.3f);  //for bold
pcb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE);
pcb.endText();    //must

//reset to normal text
pcb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL);

#其他要補充的就是,PDF的坐標計算方式,是每一頁的左下角為0,0(第一象限)...和一般程式以左上角為0,0(第四象限)不同~~
#還有其實偶搞不太懂他的計算單位為何(反正每個lib用的單位都不一樣,誰記得那麼多鬼單位!!),所以對位置時,可以先用程式在每100單位的地方做標記或先畫格線出來看一下~應該就很清楚是要加要減...
#圖片的px在讀進來轉換也是,可能是dpi的關係~先試寫縮圖看看效果,再想下一步,才不會在最後一刻才發現圖跟鬼一樣XD...

2014年12月14日 星期日

jquery ui drag on android web browser

可能是因為觸控板的關係~
所以在pad的瀏覽器下~jquery的拖拉會有問題~
還好這時候~只要加入一隻修正就好了^O^b...

好心的stackoverflow
http://stackoverflow.com/questions/5796109/jquery-drag-and-drop-on-touch-devices-ipad-android

以及
http://touchpunch.furf.com/

2014年12月9日 星期二

apache poi 3.2 hssf xls

因為某案用poi 3.2,是有點舊就是,但也發現apache poi在xls這一塊深耕了很久~
功能相當豐富,另外這個版本有個bug,就是塗Cell的背景色,所以很堅持要塗色改色的功能,就請使用新的版本吧~

但~其實xls全部硬刻是還蠻硬的沒錯!!
容易卡在很多的Font/Style/Format,很亂,要刻最好先規劃再寫會比較好...
一般大家都會推,先讀一個templet進來再替換變數來長資料進去~
不過~因為刻出來了就用吧XD...

以下大概是基本的功能匯整與smaple

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.*;

//產生主要XLS
HSSFWorkbook workbook = new HSSFWorkbook();

//產生一個XLS sheet
HSSFSheet sheet = workbook.createSheet(sheetName);


//設定字型,FontHeightInPoints (px) 會比FontSize好
HSSFFont fontTitle = workbook.createFont();
fontTitle.setFontName("標楷體");
fontTitle.setFontHeightInPoints((short)18);
fontTitle.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

//設定Cell的style,包含框線(上,下,左,右)客置與型式,內容(置左/中/右)
HSSFCellStyle rowBorderRight = workbook.createCellStyle();
rowBorderRight.setBorderBottom((short)1);
rowBorderRight.setBorderLeft((short)1);
rowBorderRight.setBorderRight((short)1);
rowBorderRight.setBorderTop((short)1);
rowBorderRight.setWrapText(true);     //若資料內容有換行
rowBorderRight.setAlignment(HSSFCellStyle.ALIGN_RIGHT);
rowBorderRight.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);

//隱藏指定的欄(開發時常會漏看這個,被害死Orz)
sheet.getSheet().setColumnHidden(col, true);

//合併儲存格
CellRangeAddress craxy = new CellRangeAddress(minX,maxX,minY,maxY);
sheet.addMergedRegion(craxy);

//儲存格數字格式
//為了排版框線會出現非常多的樣式,可建一個基本的,然後大家一起cloneStyleFrom ,就會快一點點
HSSFCellStyle rowNumber = workbook.createCellStyle();
rowNumber.cloneStyleFrom(rowBorderRight);
rowNumber.setDataFormat( workbook.createDataFormat().getFormat("###,###,##0.####"));
//儲存格數字格式 %,內建%已經自動幫你*100了,所以要記得先腦補
rowPa.setDataFormat( workbook.createDataFormat().getFormat("0.00%"));  //-->0.0001


儲存格的表示方式,其實可以先在xls上面開儲存格試打,再貼到程式裡,可以省一些預覽時間~記得儲存格式會綁在CellStyle底下,所以...這個CellStyle別拿去給不同型態的用!

//數字負要轉紅字

//顏色可以用英文寫,到xls上時,會發現他自動翻譯成中文了~
rowNumber.setDataFormat( workbook.createDataFormat().getFormat("###,###,##0.####;-[Red]###,###,##0.####"));

//格子填數字
HSSFRow row = sheet.createRow((short)rowIdx);
HSSFCell cell2 = row.createCell(colIdx);
cell2.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
cell2.setCellValue(d.doubleValue());
cell2.setCellStyle(rowNumber);

//格子填文字,HSSFRichTextString可以提供更多樣化的文字設定
HSSFRichTextString strTitle = new HSSFRichTextString(getTitle());
strTitle.applyFont(fontTitle);
celTitle.setCellValue(strTitle);
celTitle.setCellStyle(rowCenter);

//格子要變高,是改在HSSFRow裡面設定,不是Cell
row1.setHeightInPoints(30);

//使用公式,原則上就是把XLS上的公式貼來就對了,只是Word XLS copy &paste會小聰明依行數變化,poi可就要自己寫了~
cell2.setCellFormula("D2+F2");

另外說~能不能公式+合併儲存格呢~~
結論是可以的XD...

關於DataFormat
文件上寫說可以支援到4part,不過他有但書說...
Format a value according to the standard Excel behavior. This "standard" is not explicitly documented by Microsoft, so the behavior is determined by experimentation; see the tests.


打開咱們的xls 2010...會發現儲存格裡 的format2part以上...
測了一個小時多一u一a...還好情況不多,窮舉還可以...
第一個指正...
第二個為負...
第三個為0...
第四個看情況是可以不用給(若只給part3,打中文還是會顯示中文的說...。但可以用在"輸入錯誤",或是特別案例上不然只給 part3= part4 default @)
所以~這就是產品加密的好方法嗎=_=?


2014年12月7日 星期日

EC2 linux Postgresql9.3+phpPgAdmin

總算是要寫完了Orz..
到最後的Postgresql + phpPgAdmin...
身為postgresql的擁護者,就算沒用到也把它裝起來備用XDXD...

B. install postgresql 9.3
amz上提供的就到9.3而已,不過因為我也喜歡9.3,剛好~
要注意的是postgresql官網上只提供centos/紅帽/..etc的rpm...
但是~那些rpm..EC2都吃不進去!所以請用內建的就好,雖然rpm吃不進去,不過安裝設定方式都沒變,官網的資料還是可以參考~要注意的是名稱,跟以往的長得不太一樣!
https://wiki.postgresql.org/wiki/YUM_Installation

phpPgAdmin是真的找不到rpm安裝包了,但是還好~他的tar檔做得好~
只要先裝好php-pgsql後,再用tar解壓改設定,一次就過關了^_^b...

cd /etc/project
yum list postgres*
sudo yum install postgresql93-server.x86_64
sudo adduser postgres (if exist..ignore it)
sudo passwd postgres
su postgres
cd /var/lib/pgsql93/data (to check if ok)
exit (return to myuser)
chkconfig --list (check postgresql service name)
sudo service postgresql93 initdb (need sudo user to run service)
sudo su postgres
cd /var/lib/pgsql93/data
vim pg_hba.conf   (change ident to md5)
vim postgresql.conf  (listen_addresses='localhost')
exit (return user)
sudo service postgresql93 restart

B.0 create role/user
sudo su postgres
psql postgres
CREATE USER xxxuser WITH PASSWORD 'xxxpwd' CREATEDB;
CREATE DATABASE xxxdb OWNER xxxuser;
\q

B.1 install phpPgAdmin
sudo yum install -y php php-pgsql
sudo service httpd restart


cd /...somewhere..../www/html
wget http://downloads.sourceforge.net/phppgadmin/phpPgAdmin-5.1.tar.gz
tar -zxv -f phpPgAdmin-5.1.tar.gz
mv phpPgAdmin-5.1 phpPgAdmin
vim phpPgAdmin/conf/config.inc.php

host --> 127.0.0.1

##test php page

在最後的最後,環境安裝完後,要記得設定service開機啟動~~
和一般的linex操作一樣~
chkconfig --list
sudo chkconfig --level 5 postgresql93 on
sudo chkconfig --level 5 mysqld on
sudo chkconfig --level 5 httpd on

EC2 linux apache httpd+php+MySql+phpMySqlAdmin

先說,EC2 linux 跟Centos不一樣,他的yum內容,是amz自己維護的,所以其實有很多東西有少,或者名字不一樣~所以~當初看到他的廣告宣傳說有MySQL...Postgresql就很高興的選了...然後就發生了一些事情|||Orz...

EC2上面好像沒有Centos,可能是怕搶到紅帽的飯碗,另一個要免費的就是ubuntu。
其實ubuntu也不是不好,最早一開始也是用ubuntu server...但是因為某次他跟kvm還是vitural box太不合,加上當時同事也吵著要用centos...所以後來就改centos了~

在EC2要 yum時,建議先用yum查詢一下~e.g. yum list postgres*
這樣才不會一直被騙XD..因為網路上的資源大都以CentOs為主...

1. Install apache httpd
apache和 php依然是相當的快樂和平順~
記得當初開instance時,防火牆要開80,預設只有22(ssh)
話說,為啥特別把document root搬家,因為原本那邊是root的領域,換到一般user的資料夾下,統一管理比較方便...
sudo yum -y install httpd
vim /etc/httpd/conf/httpd.conf
##change document root to somewhere...
sudo service httpd start

vim /...somewhere.../www/html/index.html
##write something in index.html
##test http://xxx.xxx.xxx.xxx/

2. Install php
sudo yum -y install php
vim /...somewhere.../www/html/index.php
##write something in index.php
sudo service httpd start

##test http://xxx.xxx.xxx.xxx/index.php

3. Install MySQL
MySQL 在上面,看起來最新就只到5.5,忍著點吧O_o|||,反正用MySQL的也不會要求什麼強大的功能,基本的能動不要有bug就好!
yum list mysql*
sudo yum install mysql55-server.x86_64
chkconfig --list (check mysql service name)
sudo service mysqld start
sudo service mysqld status  (check status)
## set mysql root password
/usr/bin/mysqladmin -u root password

4. Install phpMyAdmin
這個怪打超久的,中間還死了兩三次XD...網路上普遍是說,用tar的回來自己解,然後改設定檔...嗯,這種方式以前在win上玩過是ok,但是在EC2上試,就很鬼的一直白頁面。很不想還要去開php log來de的事情,還翻到了某個日文版的攻略...重點是還裝起來了(日文有漢字真好!)只是他是很舊的phpmyadmin...但是~能用就好XD...
(說到當年要裝python一個很鳥的套件,也是日文攻略來的,這要說台日友好嗎XD)

這個方法是用rpmforge的資源,其實我後來有去看一下他最新發布的,但是phpmyadmin還是在2版,社群資源還是有差...
wget http://packages.sw.be/rpmforge-release/rpmforge-release-0.5.3-1.el7.rf.x86_64.rpm
rpm -Uvh rpmforge-release-0.5.3-1.el7.rf.x86_64.rpm
sudo vim /etc/yum.repos.d/rpmforge.repo

[rpmforge]
name = Red Hat Enterprise $releasever - RPMforge.net - dag
#baseurl = http://apt.sw.be/redhat/el5/en/$basearch/dag
mirrorlist = http://apt.sw.be/redhat/el5/en/mirrors-rpmforge
#mirrorlist = file:///etc/yum.repos.d/mirrors-rpmforge
enabled = 0                             <---edit
protect = 0
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmforge-dag
gpgcheck = 1
priority = 10                         <---edit

yum -y --enablerepo=rpmforge install phpmyadmin
sudo vim /usr/share/phpmyadmin/config.inc.php



$cfg['blowfish_secret'] = 'a8b7c6d';    <---原本是空白,隨便找一個範例塞的

sudo vim /etc/httpd/conf.d/virtualhost.conf
<VirtualHost *:80>
    alias /phpmyadmin /usr/share/phpmyadmin/
    <Directory "/usr/share/phpmyadmin">
        Options ExecCGI
        AllowOverride all
        Order Allow,Deny
        Allow from all
    </Directory>
</VirtualHost>

##restart httpd
sudo service httpd restart

在這個版本的phpmyadmin裡,他的phpmyadmin.conf,有寫了多重路徑都可以轉到,覺得有需要改就改吧:)



EC2 linux timezone, jdk6+tomcat7

這篇不太有營養,不過加減記錄一下...

大家都知到amz server的特色就是,因為主機在國外,然後台灣的市場是相對的小小小...
所以再怎樣,AMI(Amazon Machine Image)要找到台灣不用改就能用是非常的困難...
(想起當年裝windows時,亞洲版本少的可憐,裝到日文版的...日文介面是非常的新鮮,完全看不懂XD...還好跟win server2008還算熟,反正跟中文版項目順序是一樣的,拿著中文的畫面找日文的功能就是了...)

所以時區就很重要了~官方也有特別寫了一篇~驗證後他沒亂寫XD...
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html
先確認一下timezone的全名與位置,台灣還是用台北Q_Q..就降子...
tzselect 最後會帶出UTC和地區時間,可以確認他時間有沒有對後再用。
## check timezone name and datetime
tzselect
## --> Asia/Taipei
vim /etc/sysconfig/clock

ZONE="Asia/Taipei"
UTC=true

sudo ln -sf /usr/share/zoneinfo/Asia/Taipei /etc/localtime
sudo reboot

裝jdk6...嗯,因為裝到爛了,基本上我jdk都是用bin檔裝的,然後在要起Java的sh檔上,加上指定jdk的執行,所以倒也不會搶了openjdk的飯碗...
Tomcat也習慣用zip(tar),就好像也沒啥特別好講的XD...

install jdk6
##use SFTP (use no pwd private key) to upload file
cd /etc/project/jdk
cp ~/software/jdk-6u45-linux-x64.bin .
chmod +x jdk-6u45-linux-x64.bin
./jdk-6u45-linux-x64.bin

change default jdk
alternatives --config java
#choose jdk-6xxx
java -version
javac -version

install Tomcat
## upload tomcat.tar to home
cd /etc/project
cp ~/apache-tomcat-7.0.26.tar.gz .
tar -zxv -f apache-tomcat-7.0.26.tar.gz
vim bin/startup.sh  (assign JAVA_HOME)
JAVA_HOME=/etc/project/jdk/jdk1.6.0_45
bin/startup.sh

EC2 linux user manager

當第一個user可以連上EC2 server時,接下來就得好好處理一下帳號問題~
每次建server時,都會生出一堆user和password...
好幾組的帳號密碼一定要先開txt記下來~不然一定會發生搞笑事情XD...

參照官方說法
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/managing-users.html

一開始的ec2-user是有sudo權限的~也只能先用他先把帳號跟設定的部份處理掉在說~

1. create account
##先改掉ec2-user的密碼,反正也不知道是什麼,先改個自己知道的
sudo passwd ec2-user

##建立自己需要的user
sudo adduser myuser
sudo passwd myuser

2. add myuser to sudoer (not sudo group)加入後馬上生效,不需要重開機
##先把自己的user加入sudoer,很重要!!
sudo vim /etc/sudoers
##append bottom
myuser ALL=(ALL) ALL

3.  create myuser rsa private key/public key
後來想一想,在公有雲上面,使用囉唆的RSA是相對的安全,所以就照原本的用法吧~
要注意的是,在os上限制登入者的憑證的權限必須是700/600,總之就是只能專屬原所有者使用!
3.a create myuser ssh authorized_key
sudo su myuser
mkdir .ssh
chmod 700 .ssh
touch .ssh/authorized_keys





3.b generator myuser's public/private key..
這個和EC2上面的key pair應該是一樣的,但是官網的寫法很奇怪,總之是用別的solution
##create tmp folder
mkdir pem
cd pem
ssh-keygen -b 1024 -f myuser -t dsa
##passphrase just ENTER (without password)
##myuser(private key), myuser.pub(public key)
ls -l  (will see myuser and myuser.pub)
chmod 600 myuser.pub
cd ~
cat pem/myuser.pub >> .ssh/authorized_keys

3.c create putty ppk
vim pem/myuser
##copy all text from terimal console to local pc txt, named myuser.pem
##run puttygen
##load myuser.pem
##generator and save private key(myuser.ppk)
在這邊就是做出私鑰來用,但是有一點是,若是有需要用SFTP...
因為我習慣使用的是FileZille Client,但是他的憑證不支援多一道密碼保護的,
所以若是要用SFTP,那就不要用passphrase,空白就好
還有就是公私鑰似乎會認檔名,檔名不能亂改,所以就用同名的就好~
連putty使用私鑰的方式就一樣了,但是注意只能用在此單一user...
(當初想偷懶全部user用同一套公私鑰~但就是一直有問題,分開各自設定就好了!)

3.d SFTP by FileZilleClient
test SFTP
編輯->設定->連線->SFTP  就是把ppk放進去

連線時,就會解開了,然後只要打user name就可以,跟平常的SFTP一樣~

4. ssh connect and test
因為我習慣使用pietty,pietty會記錄連線資料,所以要是確定平常用的那個user就可以用pietty設好,以後就很方便~
不過在東西還剛開始的時後,還是建議用putty...雖然他每次連都要重設,重新指定一次,
但是因為在一直測不同user...一直換key的檔案也沒差XD...這時putty就比較好用!

補一下putty的設定連線方式圖
檔案放進去後,再打ip


黑白畫面就是打帳號名稱就是了。
接下來就是確定myuser是否正常,而且要確定是否有sudo權限。
(千萬別還沒試出自己的sudo就去把ec2-user給砍了,那會很搞笑的XD)

5. disabled ec2-user sudo
記得先設定好自己的帳號,因為我不習慣用root,也不習慣開放root來用。
所以一向都是以sudoer在做處裡。EC2預設也是不能用root 登入,所以也不用特別去改~
反正RSA就在前面會先踢人了!沒設定RSA的user連第一關都到不了XD...就不管他了~
確定自訂的user都能動之後,就準備把ec2-user給降級了~是說可以砍了他...
不過我是覺得倒也不用這麼狠...而且他本來就是一般user,amz做了點手腳讓他有sudo而已~只要把他的設定檔弄走就好了,重開機後才會生效,所以要是想保有ec2-user的sudo在還沒重開之前還是有救的~~
## here is ec2-user can be sudoer
sudo cat /etc/sudoers.d/cloud-init
## rename/remove or delete the file...
sudo mv /etc/sudoers.d/cloud-init xxxxxxx
##  disabled sudo after reboot
sudo reboot

## login ec2-user to check if sudoer


2014年12月6日 星期六

Linex EC2 Connect

最近打算研究一下amazon的EC2...
之前也有小玩過~不過都是跑WinServer...

因為錢錢的問題...所以~也是該研究一下linex怎麼弄低~

建instance的方式都差不多~很快的就生出instance...
但~是~~

怎麼連上去開始出問題了Orz..他沒像Win會給你Administrator的密碼...
為了搞出這個畫面,又奮鬥了一個小時Orz...

大概幾個要注意的地方是~

### get .gem file
在建instance的最後Review畫面,它會問你要不要給一組key...為了安全性的問題,記得一定要選自訂的... 或是自動產生一組,會拿到一個xxx.gem 的檔案。

### .gem -> .ppk
把gem存起來~要用putty系列工具...
http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
這裡除了putty外,還有 puttygen.exe
利用puttygen.exe Load .gem檔,然後另存為 private key
我習慣都會打密碼,因為這些公開的流程大家都知道,想什麼保護都不做就太天真了!

### putty import .ppk
打開putty...Connection->SSH->Auth,基本的勾勾不用什麼改
在底下的檔案位置把.ppk 擺上去,確定

### connect server
雖然看到網路文章連的位置是dns還什麼的,不過我就直接連IP...
不知道default user是什麼,總之先從amz給我的提式開始猜一_一|||
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/putty.html

矇對後,會再跳出一行要打key的密碼,就用.gem轉.ppk時的那組,登登~~
總算看到歡迎畫面了Orz...

接下來~繼續奮鬥,但是至少~應該就有規則一點了吧XD...

2014年11月26日 星期三

jsf datatable row background


前題一樣是 jsf iceface 1.8...
這次是關於datatable 填背景色的問題~

一般最常用的就是資料每列換個底色~一灰一白,讓每一列可能更突顯出來~
在jsf是個friendly的設定
<ice:datatable ........................rowClasses="mystyle1,mystyle2" .....>
使用rowClasses,","分隔(他對於顯示方法有他自己的一套定義,跟CSS公開的selector寫法不同~喵的說少學一套,對偶來說跟本是要多記一套=_=),他就會依照順序一直重覆下去,如果想做三色或是七彩漸層就是...mystyle1,mystyle2, mystyle3, ..., mystyleN

然後要知道一點~搭配paging的時候,下一頁還是照順序~
ex: rowClasses 設定1,2,3...一頁顯示筆/共20筆,
page1: 1231231231,page2:1231231231...
每一頁都一樣啦~~~

所以如果只是某一列資料要變色,如果想指定第一筆變色,或當其他條件變色時~
就...雖然google告訴偶有人說~
rowClasses="#{ myrow.check == 'A' ? 'mystyle1'  : 'mystyle2'}"
但是對我來說就是not working <--最近這個關鍵字都快打爛了一_一(不只jsf造成的)
不過有人打勾,可能是其他版或其他plugin吧一.一a...反正...

你知道的~在規則之外只能使出骯髒手段了...


方法一:
如果資料列數不多,就把dataPaginator 改成scroll bar的方式,或是加大datatable row到全筆數,總之就是不做分頁。
把class用自訂的後端bean參數帶入 rowClasses="#{bean.tableCssList}"
在此屬性只能為 String type,內容一樣用","分隔
public String getTableCssList()  {
    return "style1,style2,style2,style2,style2,style2,style2,style2,style2";
}


方法二:
前者限制較多,但是程式相對簡潔,符合少學一樣的做法,
接下來的方式是對前端override的方式~不需要後端~
改在column上,等於對table td cell改,缺點是每一個column都要貼一次(因為要補滿一排)
然後某些項目需要用!impotant做覆寫才會成功
<ice:column styleClass="#{ myrow.check == 'A' ? 'cellstyle1'  : 'cellstyle2'}">
.......
</ice:column>

.cellstyle1  {
    padding: 0 0 0 0;
    background-color: red;
    text-align: center !important;
}

.cellstyle2  {
    padding: 0 0 0 0;  
    text-align: center !important;
}


方法二是偶最後採用的做法,彈性比較大,雖然多加了CSS...因為他又粉爛的是
column的 style和styleClass 不能同時使用(這又是什麼神邏輯,output成html時,不就append有這麼難嗎!!)
優點是雖然code比較多,但是未來可以做彩色磚一.一+...而且不受分頁影響
其實網路上大家也都有提到二的解法,差別只是因為大家都只改顏色,沒把padding刪掉就以not working告終...

唉,是誰說學tag就可以不用學CSS/JS的,用tag反而得用更高深的基礎才能應用!


2014年10月23日 星期四

jsf valueChangeListener bug

這個問題我認為是個BUG...
就是當 select 來valueChangeListener時,切換bean的資料,
會發現很詭異的inputtext...

假設案例是selector onchange時, 要同時把 OOO(inputtext)改成 "bala"~


當OOO是disable="true" 時,資料會寫上去,有bala~
但是普通輸入時不會(diabled="false")...沒有bala~然後在頁面把內容output出來是沒改的!!
這鬼恐怖的地方在於,在某些頁面(相對簡單)是會動的...
但是有些頁面又不會動....

查了很久~總算看到一個應該是比較好的解就是~
http://stackoverflow.com/questions/14171493/how-to-get-updated-model-values-in-a-valuechangelistener-method

public void changeSelector(ValueChangeEvent event) {
        if(event == null || event.getNewValue() == null)  {
            return ;
        }
       
        if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
            event.setPhaseId(PhaseId.INVOKE_APPLICATION);
            event.queue();
            return;
        }
       
    // Do your original job here. 
    // It will only be entered when current phase ID is INVOKE_APPLICATION.
 
        String key = (String)event.getNewValue();
        this.setXXX(key);
        this.setOOO("bala");
}

另外select (selectOneMenu)
有些鬼是,明明選了項目,但是它鬼資料就是不會跳到你選的選項~
(鬼打牆一直跳在第一個)
注意它的 id 必須要和 bean裡面的property naming 一模一樣!!
感覺很有鬼~但是沒辦法~它就是爛~___~|||
<ice:selectOneMenu id="selector" value="#{bean.selector}" ....



2014年10月14日 星期二

iceface jsf 日期 少一天問題

因為他是個BUG嗎?....

總之因為當初他在 convertDateTime 的實作裡,把時區寫死成GMT還UTC忘了(總之就是格林威治時間)...
所以造成了很多很多很多很多的困擾...

話說找這問題的關鍵字還蠻難猜的~ 後來總算找到是..."jsf convertdatetime one day less"

解法是蠻多種的~但是....卡版本就要哭哭了...

看起來是JSF 2.0以上的版本可以用設定檔改成主機時區
<context-param>
    <param-name>javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE</param-name>
    <param-value>true</param-value>
</context-param>
 
或是基本的...直接寫死時區(timeZone大小寫要對喔,寫錯還不會出來=_=|||)~
但是看偶幾十頁要全重改一次就是很不爽一x一...
<f:convertDateTime pattern="yyyy/MM/dd" timeZone="GMT+8" /> 

然後用這個的除了文字顯示外,還有小日曆要一起修的問題~
所以就很不爽(假裝很厲害)的去寫了一個converter出來...
此法也只適用於jsf 1.6以上的樣子,看討論是因為
<f:converter converterId="XXXXXXXX" />
這種寫法, 1.6後才支援...
 
重點大概是~一定要繼承 DateTimeConverter,小日曆裡才吃得進去!!
改掉萬惡的時區。
getAsObject裡,一定要回傳java.util.Date,不然小日曆就擺臭臉給你看....

然後既然都自己刻了,就也順便修一下原本沒事就亂丟NullPointerException的問題...

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.DateTimeConverter;

public class DateInputConverter extends DateTimeConverter implements Converter {
 
 SimpleDateFormat sf = new SimpleDateFormat("yyyy/MM/dd");

 public Object getAsObject(FacesContext context, UIComponent component, String value) {
  java.util.Date re = null;
  try  {
   if(value != null && value.length() > 0)  {
    re = sf.parse(value);
   }
  }  catch(Exception e)  {
   
  }
  return re;
 }

 public String getAsString(FacesContext context, UIComponent component, Object value) {
  String re = "";
  try {
   if (value != null) {
    // format Timestamp
    if (value instanceof Timestamp || value instanceof java.util.Date) {     
     re = sf.format(value);
    }
   }
  }
  catch (Exception e) {
   e.printStackTrace();
  }
  return re;
 }
 
 public TimeZone getTimeZone()  {
  return TimeZone.getDefault();
 }
}

face設定檔裡面也要加自定的converter
<converter>
    <description>小日曆輸入 yyyy/MM/dd</description>
    <converter-id>formatDateInput</converter-id>
    <converter-class>XXXXXXX.DateInputConverter</converter-class>
</converter> 

前端page
<ice:selectInputDate id="birthday" renderMonthAsDropdown="true" 
    renderYearAsDropdown="true" renderWeekNumbers="true"
    value="#{bean.birthday}" title=""                             
    renderAsPopup="true">
        <f:converter converterId="formatDateInput" />
</ice:selectInputDate>  


2014年10月3日 星期五

jsf + iceface

老實說~第一次寫前端寫到爆氣都是因為這種很難用的東西....
用後端寫前端~講得好聽是少學一種語言,但是多學一個不通用(有BUG)的語言更痛苦!
剛想起來,應該不只多一個語言的量,還包含tag function/el/logic/selector....

目前發現到的主要問題在於,submit時有時候他會lose掉內部存的bean資料(看到一堆資料莫名的被清空~會哭阿T_T~FF的出現率比IE高很多~Orz...不知道是不是跟cache還什麼有關)~看看討論,有人說用immediate來處理,但是好像還是無法完全避免(偶爾發生,偶爾不會~|||Orz)

另外就是排版問題~因為無法去設定該原件在前端是個什麼樣子,有些元件的width調了也沒用(可能有些是span, 有些是div)問題是不大,但是在某些硬要排出來的情況就會哭了~加上偶這系統是完全以table為主切的~span/div在td內的行為有些本來就會不正常。在顯示或隱藏時,有時候得再多包一兩層的table去讓他變成期望的樣子,老實說有點不爽....(小時後受過古早IE的table摧殘,現在正是發揮作用的時候嗎T^T)

至於因為是tag...所以本身支援的function與operator就比較少,有時候想到一個什麼大家都有的,像if/else這種東西很基本的在上面硬要寫就又讓人不爽=_=...雖然還是可以找繞遠路或長落落的寫法來處理~但就是會不爽~ (當年tag是為了美編設計而發想的設計,但是我覺得那些rule反而比一般程式的還難,美編要是學得會還不如當前端工程師算了!)

因為該架構是用iceface的擴充版~沒聽錯的話,偶這程式是1.8的樣子...
reference sample url :
http://component-showcase.icesoft.org/component-showcase/showcase.iface 

iceface的reference很重要~要先看範例再問問題比較好....因為有很多浪費很多時間的問題~其實只要選對屬性就好了...但是還是浪費了很多時間在找解....
另外是擴充版也好幾個版本~直接問google常翻到別人的解一.一|||也沒屁用....

註記幾個用到的,不一定純ice或純jsf

###小日曆輸入
很妙的這個,在FF上看寫英文(Sun),用IE寫中文(星期日)~後端只吃java.util.Date
這東西的缺點在排版上,他產生時大概是個div...所以後面的字會被換行=_=...只好硬生生的又做了個sub table包起來~
<ice:selectInputDate id="myDate" renderMonthAsDropdown="true" renderYearAsDropdown="true" renderWeekNumbers="true"
                             value="#{bean.myDate}"
                             title=""
                             renderAsPopup="true">
                    <f:convertDateTime pattern="yyyy/MM/dd" />
                </ice:selectInputDate>

###檔案上傳
不知道是好用還是難用的東西,屬性設定變化就能有很大的不同,好用。但是屬性內的width看起來是壞了,設定不是中間的input變短,而是直接切掉,若像我是用加按鈕才上傳的話,按鈕被切掉是要怎麼傳啦=_=a...

另外上傳的路徑就直接設定在tag裡了,不知道該說好用還難用(不管你要不要這個檔,他都直接寫上Server)...後端是直接處理實體file了,而不是stream! 另外測試~中文檔名也可以正確的寫出來還不算笨~在此~相信apache的FileUtils會是個很好的搭檔~

submitOnUpload最好依情況設定比較好
<ice:inputFile id="inputFileName1" autoUpload="false" label="上傳"
                       uploadDirectory="/upload/temp/" submitOnUpload="postUpload"
                       actionListener="#{bean.uploadFile1}"
                       immediate="true"/>

public void uploadFile1(ActionEvent event) {
        InputFile inputFile = (InputFile) event.getSource();
        FileInfo fi1 = inputFile.getFileInfo();
        String absPath = fi1.getFile().getParentFile().getAbsolutePath();
        if(checkUploadFile1(fi1))  {
            try  {
                FileUtils.copyFile(fi1.getFile(), new File( absPath+"/"+ afterName));               
            }  catch(Exception ee)  {
                ee.printStackTrace();
            }
            ........   
        }
    }

###datatable group by
為了找個顯示上的解法,花了半天google,結果發現只要寫一個屬性~真是給它無言...
其實這個就是把值相同的欄位做 rowspan
groupOn的內容值,可以不等於底下的output,可以把id2想成是一個hidden的欄位~
<ice:column groupOn="#{row.id2}">
                                    <f:facet name="header">XXX</f:facet>
                                    <h:outputText value="#{row.id}" />
                                </ice:column>

###datatable 不同行換顏色
跟上面的原本是差不多的意思,只不過因為後來覺得有做rowspan,大家的眼睛不會看花之後,就沒有來搞顏色區分了~不同解法有看到就是了~
基本上就是rowClasses,用","隔開,就會出來了~
<ice:dataTable rowClasses="row_odd, row_even" ...



2014年9月18日 星期四

jsf 備忘

雖然討厭jsf...討厭要用後端寫前端的鬼...不過碰到的系統是這種鬼就沒辦法~_~|||
原架構完全不能寫javascript(js)的!!改個小function也要重起server, 真的很想翻桌!!

為了寫連動,動態即時顯示,就看他一直在轉圈圈...轉圈圈...轉圈圈...
還常常是browser cache還是反應不來內容,丟舊的值或是沒反應又以為寫錯~總之就是很難用拉T___T....
後端也得配合寫一堆method...明明用js的this之類的就可以解決一串T^T....

### jsf 的空白
就像html裡的 &nbsp;
jsf裡用 &#160;

### jsf 傳值的方式~
這篇真的寫得太好~來查了好幾次XD
http://www.mkyong.com/jsf2/4-ways-to-pass-parameter-from-jsf-page-to-backing-bean/

我都用setPropertyActionListener  直接把值注到bean裡來用~
<h:commandButton action="#{user.editAction}" >
    <f:setPropertyActionListener target="#{user.action}" value="delete" />
</h:commandButton>

### jsf if, if else
在jsf裡的奇怪世界,如果是要實現 if 的情況~ 可以用容易懂的
<c:if test="#{bean.aa == '1'}"> ............ </c:if>

但是如果是要 if else 的話,就要改用 choose....
<c:choose>
    <c:when test="#{not empty bean.aa}">
        BALA               
    </c:when>
    <c:otherwise>
        BALA2
    </c:otherwise>
</c:choose>

但是如果想要用 if else if else if else...這種的話呢?~
目前看起來沒有~就只能給他一路if下去....
<c:if test="#{ not empty bean.aa }"> OOO </c:if>
<c:if test="#{ bean.aa  == '1' }"> XXX </c:if>
<c:if test="#{ bean.aa  == '2' }"> ZZZ </c:if>

然後更妙的是,在UI元件上,想要顯示或不顯示~不可以用 c:if 之類的要不要show...常常會出問題......div block之類的好像還可以就是....
就要用html/component元件 裡的 rendered 來表示~
<h:graphicImage url="/images/xxxx.gif" rendered="#{fn:trim(bean.aa) eq '1'}" />

講到trim這個function...只能說~jsf 的function不是到處想用就能用Orz..
僅量在後端model就trim好丟出來,省得碰到某些地方不給trim就GG了....
(討厭資訊庫開CHAR欄位的東東,沒事自己補空白都會造成很大的困擾...VARCHAR不是很好嗎>.<b)

### jsf number format
inputText 記得要 partialSubmit 才會動
<ice:inputText value="#{bean.aaa}" id="aaa"  partialSubmit="true">
            <f:convertNumber pattern="######.####"/>
</ice:inputText>

<ice:outputText value="#{bean.aaa}" >
            <f:convertNumber pattern="######.####"/>
</ice:outputText>

### iceface 小日曆寬度
小日曆佔的位置太大~可以改寫CSS變小
<style>
.iceSelInpDatePopup {
    min-width: 120px;
}
</style>

### iceface  selectOneMenu(下拉選單)
若需要在其送出時多送其他的值給在ValueChangeEvent抓到,可以參考以下用法
其實我是用在datatable裡,所以f:attribute 是傳那個row的 key
jsp:
<ice:selectOneMenu value="#{bean.dealType[row.key]}" valueChangeListener="#{bean.changeIdDealType}" partialSubmit="true">
        <f:attribute name="more" value="#{row.key}" />
        <f:selectItems value="#{ctrl.lstId}" />
</ice:selectOneMenu>
java:
String more = ((UIInput)event.getSource()).getAttributes().get("more").toString();
String newId = (String)event.getNewValue();




2014年8月27日 星期三

繼承struts select tag 改label

為解和前篇一樣的問題,置換的內容除了property外,還有select。
所以只好再code一個select出來了~

原則上還是以原本架構為主體,但是把資料的model(就是selectTag裡的 list 內容換掉),因為又不能改bean/vo,就不能set回去...所以將以現成的request的setAttribute/getAttribute來放改過的資料~就不用動其他的程式

1. 繼承SelectTag
public class MySelecStrutsTag extends SelectTag  {

2. 定一個tag attribute來接原始資訊(即原本寫在list的那一項改到這邊來)
private String orginList = "";

3. override populateParams,別忘了super(它做了很多要set的東西),要偷換資料的內容就接在底下做
    public void populateParams() {       
        super.populateParams();

4. 先用 orginList 裡找出原值(fVal),改一改再塞回去(lst)
        String expr = ComponentUtils.stripExpressionIfAltSyntax(getStack(), this.orginList);
        Object fVal = this.findValue(expr);
       
        //......................
        String exprNew = ComponentUtils.stripExpressionIfAltSyntax(getStack(), this.list);
        getStack().getContext().put(exprNew, lst);

5. 前端要設定tld,除了自訂的attribute要加進去外,別忘了去找一下struts tld裡面的SelectTag,有一大串長落落的也attribute也要copy進去

6. 重起後就可以在jsp上面呼叫囉~因為是用request的attribute來暫存,所以tag attribute的寫法要寫成 list="#request.tmpData" ,#request表示調用request.getAttribute


過程是有點血淚..因為原本想要塞值回原bean,後來發現原本的那個bean沒做setter,再怎樣也擠不回去Orz...然後為了把值丟在struts能find的方式~也是把一堆setXXX全拿來試了一遍~\囧rz...
    
   

2014年8月26日 星期二

繼承struts tag取值

這篇是與前一篇有關的(簡易解析struts property tag 取值 )~

前一篇是用JSP的概念去parser struts的格式資訊,只需要J2EE的method就可以~
阿這一篇就是採用struts自己的功能來取,就是call struts的method...
(雖然有點懶惰~但還是去看了一下原始碼來用用~之後還是得靠他自己內部來call取值,在loop時,用原始方式就沒辦法拿到外部變數了Orz..得靠struts tag內的暫存才有辦法取得到)

1.  先找一個struts tag來繼承,先選最簡單的來練習
public class MyStrutsTag extends PropertyTag {

2. 通常有繼承,constructor照跑比較安全
public MyStrutsTag()  {
    super();
}

3. override doStartTag() ,這裡想了有點久,因為在原始碼內,doStartTag其實會call protected void populateParams(),看原始碼大家通常是改在這裡,但是因為我需要改寫最後輸出的out的內容,後來決定就在這邊先跳掉,讓流程SKIP_BODY

public int doStartTag() throws JspException {
        //前面這裡都照抄,讓該讀的資料都進來,也不知道有沒有用到,只是萬一要用就有
        ValueStack stack = getStack();
        component = getBean(stack, (HttpServletRequest) pageContext.getRequest(), (HttpServletResponse) pageContext.getResponse());
        Container container = (Container) stack.getContext().get(ActionContext.CONTAINER);
        container.inject(component);
       
        super.populateParams();

4. 開始改寫加入自己的東西,調用struts內的值,val要先處理到只剩取值表示法那樣:example: "result.dt",正常的話fVal就應該會是像struts tag取出的值了~

                  val = this.findPropertyValue(val);   
                  String expr = ComponentUtils.stripExpressionIfAltSyntax(this.getStack(), val);
                  Object fVal = this.findValue(expr);

                  if(fVal != null)  {
                      val = fVal.toString();
                  }


其實重點也就是只有,使用ComponentUtils,還有暫存資訊的getStack()...
另一點是還沒看到它解tag的attribute,所以~解tag的方式我還是用string->dom的方式取value

簡易解析struts property tag 取值

起因在於有莫名的堅持要弄一個純"Tag"的網頁...因為這郭系統是不做js/ajax的,很傳統的java網站(就是輸入值都要submit到server做檢查的那種~~)
然後講一講後端邏輯的部分不改,就只改前端的view.....造成只好要用自訂tag( custom tag)包掉原本的資料~

原本的資料本來就是動態的撈出來的~所以原本值就已經被struts的tag包起來,學struts包struts那樣子,想說包兩層應該很簡單(包兩層只要一個用小撇('),一個用("),他就會分出來了)~

可是我忘了偶自己寫的不是struts tag阿阿阿阿阿~~~~~
實作自訂Tag時,JSP也很乖的送了一字不漏的struts tag的文字進來了Orz...
最後想不出辦法來,只好硬解tag內容抓值了,就以會傳入的型式自己寫parser了Orz...

1. 先解析struts tag,反正就只抓property 的value
2. 輸入值抓出來後,就依其定義在JSP裡面抓值,只處理像這種情況的取值result.startDt.substring(0, 4),只有一層bean,也不處理array情況,bean也只能get一次,原本的定義應該是用while loop可以一直抓下去的~
request.getAttribute() --> bean --> getter --> substring
3. 前端呼叫的寫法
<my:year format='YYYY' value='<s:property value="result.startDt.substring(0, 4)" />' />
4. tld別忘了要做tag設定才會被讀入~

String getStrutsPropertyValue(String val)  {
        String re = "";
        Document doc = null;
       
        ByteArrayInputStream bis = null;
        try  {
            val = "<root>"+val+"</root>"; //append to a normal xml format
            bis = new ByteArrayInputStream(val.getBytes());
           
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            doc = dBuilder.parse(bis);
           
            NodeList nl = doc.getElementsByTagName("s:property");
            if(nl.getLength() > 0)  {
                String theValue = nl.item(0).getAttributes().getNamedItem("value").getNodeValue();
              
               String[] sp =  StringUtils.split(theValue,".");
                if(sp.length > 0)  {
                    Object obj0 = pageContext.getRequest().getAttribute(sp[0]);
                    if(obj0 != null && sp.length > 1)  { //attribute is bean
                        String getter = "get"+sp[1].substring(0,1).toUpperCase()+sp[1].substring(1);
                        Object obj1 = obj0.getClass().getMethod(getter).invoke(obj0, null);
                        if(obj1 != null)  {
                            re = obj1.toString();
                        }
                        if(sp.length > 2 && obj1 != null)  {  //string operation with bean property
                            if(sp[2].indexOf("substring") >= 0)  {  //support substring
                                String[] agrs = StringUtils.split(sp[2].substring( sp[2].indexOf("(")+1, sp[2].indexOf(")")),",");
                                Object obj2 = null;
                                if(agrs.length == 1)  {
                                    obj2 = obj1.getClass().getMethod("substring", int.class).invoke(obj1, Integer.parseInt(agrs[0].trim()));
                                }  else if(agrs.length == 2)  {
                                    obj2 = obj1.getClass().getMethod("substring", int.class, int.class).invoke(obj1, Integer.parseInt(agrs[0].trim()), Integer.parseInt(agrs[1].trim()));
                                }
                                if(obj2 != null)  {
                                    re = obj2.toString();
                                }
                            }
                           
                        }
                       
                    }  else if(obj0 != null)  {  //only attribute value
                        re = obj0.toString();
                    }
                }
            }
           
        }  catch(Exception e)  {
            e.printStackTrace();
        }  finally  {
            if(bis != null)  {
                try  {  bis.close();  }  catch(Exception ee)  {}
            }
        }
        return re;
    }

很久沒用java.lang.reflect,是有點手癢XD...那個package真的很有意思~~

PS:雖然說 jdk後來同意 Integer, int 這種變數的型態可以互通,就是寫程式的時候大I,小i亂塞是被complire認同的~但是在Class的定義上~ Integer.class =/= int.class~所以在找method的時候~ 還是得用正確的型態才能找到喔!

PS:java內建的split還是常常不好用,用apache.commons.lang.StringUtils安定!

PS:原本也想直接找struts的Tag做繼承,不過它看起來還是得自己去解tag字串,要仿做一個完整的就變成要去研究它的source code和呼叫關係才行~這~實在有點麻煩就先用爛解處理了一.一a...反正能動就好!

2014年8月19日 星期二

Crystal Report-小筆記

純筆記小東西~
外國的東西中文版通常會更難用~都是因為那個奇怪的翻譯Orz...
在google的時後也會造成困難~因為都看不懂他們是在說那個Orz...

###群組,日期
群組就新增群組應該沒什麼問題~但是若是該欄位為日期時要注意...
它group by 的項目default是周...(為什麼是周阿阿阿阿阿阿~超故意)
要記得去改他,一般最常用應該是日,月,年才是~

###打SQL的地方
資料庫專家(這翻譯一看就超不專業的~)--> 命令(右鍵修改)
--> 修改命令視窗

左邊區塊就是打SQL的,照規定打就好沒啥問題~
重點在右邊~ ~若左邊有where條件的話,通常都會有一堆$變數傳入的地方~~
傳入的變數"一定"要在右邊"建立"一個與變數一模一樣的項目~才能真正的傳值進去...

確定後~如果沒啥問題~就會跳出變數資訊輸入值給你填(就算沒報錯,也不一定表示一定對,只是有過語法而已)~記得這個與報表設計右下的"欄位總管"的參數是迷有關係的!!雖然,在資料庫建完右邊參數後,欄位總管會出現看起來一樣的東西,但是用途是不一樣的!!
我猜是把輸入DB的參數再代入一般變數到ap(reprot)裡使用,這邊已經是後段了,與DB的條件內容輸入無關~改"欄位總管"的項目是不會改變DB的查詢條件的!!

###撰寫function(公式)
其實很簡單的~右鍵新增或隨便找個東西編輯修改~
就會跳出"公式工作區-公式編輯器",重點在於中間與右上方default會有"報表欄位"、"函式"、"運算子"。樹狀點開後~可以直接把該項目用滑鼠拖拉到下方的打字編輯區,就會帶出該項的template!

對於拉變數欄位還蠻方便的~不用去記,因為欄位/參數/公式會全在 "報表欄位"裡~
而函式這東西就是讓你看看有沒有什麼可以用的...找不到就是沒有就對了!早點死心Orz...
運算子也還算實用~實用的原因是~它的語法真的太奇怪了阿阿阿~~不知道是那位天才想出來的,很奇怪的function概念~

在我目前的感覺上~嗯一"一a...因為偶又沒看過書,沒上過課,直接當沒說明書的遊戲在打王...就~
以var變數上來說,定義時不要給init值,當有要用到時,沒值他會自己起一個default值(總之要用的時後不會是null啦~)
function裡,其實他理論上像是 string xxxx(), int xxxx()的那種樣子,會要求你一定要給回傳值讓他顯示在頁面上~但是也沒有return的寫法,就是...只要隨便用個變數給個值~或是給它個純字串純數字~他最後就會把最後的那個值顯示在報表上~只能說"天才"Orz...

###數字轉文字時加三位一撇~
ToText( cnt ,'##,###,###,##0', 0);

###判別null...在crystal report內沒有null這種東西,是用預設值在看的~
http://stackoverflow.com/questions/18691542/how-to-check-for-null-condition-on-shared-variable-in-crystal-reports

Number: 0
Currency: $0
String: "" //The empty string
Date: Date(0,0,0) //The null date value
Time: None or null //The isnull() function won't work
Datetime: None or null //The isnull() function won't work

所以以日期舉例來看為~
if  ({命令.MAX_DATE} <> Date(0,0,0)) then
    ToText ({命令.MIN_DATE},'yyyy-MM-dd')
else
    ''

### 日期轉民國年
(其實,後來發現內建的"欄位格式",就可以改成民國年format了,不過要是資料源是字串,還是得自己處理)
if  ({命令.MIN_DATE} <> Date(0,0,0) And {命令.MAX_DATE} <> Date(0,0,0)) then
   ToText(ToNumber(Year ({命令.MIN_DATE}))-1911,'####')+'.'
    +(Mid(ToText ({命令.MIN_DATE},'yyyy.MM.dd'), 6,5))
    +' ~ '
    +ToText(ToNumber(Year ({命令.MAX_DATE}))-1911,'####')+'.'
    +(Mid(ToText ({命令.MAX_DATE},'yyyy.MM.dd'), 6,5))
else
    ''
### 動態where 時間區間,若原值可能有null,用IFNULL,default 起迄可以用Date的最大值或最小值~或是實務上的時間。動態數字,原理一樣(處理<=, >=)
....
and IFNULL(CREATE_DATE,'1800-01-01') >= CASE
      WHEN '{?CREATE_DATE_START}' != '' THEN '{?CREATE_DATE_START}'
      ELSE '1800-01-01' 
END
...

### 動態where string(處理=)
....
and TYPE = CASE
      WHEN '{?TYPE}' != '' THEN '{?TYPE}'
      ELSE TYPE
END
...

### 設計軟體的XLS對齊....
一般如果只是拉固定頁面排版或是純文字的話, 應該是沒什麼好講的...
但是如果是要拉成像XLS那種格子的話....就...

因為crystal report並沒有提供table類型的排版,所以就是用框線(口)加上直線(一|)這種東西組合出來Orz...

表頭基本上就是外面一個大框(口),加上豎線(|)。如果表頭多列的話就再加橫(一)畫出來,做成最上面的區段(表頭或是群組的區段)。

資料的話,就是在細目畫出左下右框線(|_|),上框線要空的,因為表頭或前一列會提供上框線...

相連的區段,可以用區段調整,把區段的空隙消除,就會黏在一起了~

PS: 後來發現如果用大框(口)其實也可以接成xls的樣子,但是有個前提是,大框的左右兩邊必須都是對齊的狀態,在看預覽時,就會發現有對齊的格子跟沒對齊的格子畫法不一樣O_oa...
然後實際上跑的時候,橫線有時會黏在一起,有時不會,真的超級Orz...
接著試出最後一招....在區段,右鍵「排列各行」。接著就會在邊邊出現對齊用的小三角形(然後版通常會亂一下),開始拉小三角形到定點,還有調最底下的那個往上(通常那個就是指大框(口)的底)...接著最後調整區段清掉空白(調整區段後,應該還要可以看到最底下的小三角形...就是要把那個拉比區段高一點點)...然後T__T他終於會把線黏在一起了~不過...是會比較粗啦~但至少不會空在那裡更怪~_~|||....

然後常會碰到很機車的,在那邊打XY,長度寬度怎麼打數字都會故意亂跳,自以為很聰明但是全都不對!...按左右移格的位置還都很奇怪(就是為了那0.01...)
這時候就到工具列"檔案"--> "選項" 裡,貼齊格線的勾拿掉...
http://stackoverflow.com/questions/17529383/crystal-reports-object-size-and-position-not-working
接下來後,就可以用滑鼠拖拉對齊了,但是再怎麼打數字就完全不鳥你(管你打20, 30聞風不動,一切靠200%+滑鼠...)....雖然很難用,但是這個方法至少讓我可以移動傳說中的0.01位置...

### 設計軟體字被裁切
這個可能是pdf版本所延伸出來的問題,然後中文字又喜歡長得特別胖,很容易看軟體美美的,等產出後整個走經=3=...

所以就是一開始的文字高度一定要定高出來多點,如果外框剛剛好,一定會被切到底部...
然後目前Browser看到的樣子,與另存新檔開出來的樣子也會有差別(聽說連螢幕解析度也有影響)...
如果user很龜毛的話(user通常也不懂資訊,能有個能說嘴的通常都龜毛的跟什麼一樣...)
最好還是乖乖的依實用案例量身打造囉~

Crystal Report-自訂Array動態接值

剛看到一項限制~補充一下。陣列大小最大為 1000 個元素 。
看來array真的不好用@@~

因為報表本身並不會幫你處理RAW data...雖然在產生報表時,一定是有吃到~但是卻沒有提供給人隨意取資訊的method...所以真的很難用阿阿阿(回音)

繼前篇~那是對內容每份做個小計~
但是其實在最後的部分還得給一個全部的統計~
所以......眼看又要GG了~其實最後還是GG先用subquery解決~只是因為去db撈兩次感覺還好~GG的原因是因為這報表沒有提供Sort的function,硬要刻真是太難為我了Orz...

不過還是留一下寫出來的function~
排版示意圖:

細目      @sum_add
....
報表尾  (@sum_sort) @sum_show

作法,其實就也是建兩個array,一個放名稱(all_name),一個放累計筆數(all_cnt),另一個則放array的size(idx),其兩個array就是1:1做對應。

add時,會先比名稱,有的就累計舊的,沒有就兩個array各擴充一筆
最底下的 Sum (all_cnt);是拿來做debug用的~
@sum_add
Shared stringVar array all_name;
Shared NumberVar array all_cnt;
Shared NumberVar idx;

stringVar tmp := {XXXXXXXXX} ;
Local NumberVar i;
booleanVar isContains := false ;
For i := 1 To idx Do  (
    if all_name[i] = tmp then (
        all_cnt[i] := all_cnt[i]+1;
        isContains := true ;
    )       
);

if isContains = false then  (
    idx := idx+1;
    redim preserve all_name [idx];
    all_name[idx] := tmp;
    redim preserve all_cnt [idx];
    all_cnt[idx] := 1;
   
    Sum (all_cnt);
) else
Sum (all_cnt);


最後顯示相對簡單的~就照順序拿出來,但是就在這個摩門~我想起來這邊應該要依項目排序的Orz...
@sum_show
Shared stringVar array all_name;
Shared NumberVar array all_cnt;
Shared NumberVar idx;

stringVar showData := '';
NumberVar i;
for i := 1 to idx do (
    showData := showData +Chr(13)+Chr(10)+ all_name[i] +':'+all_cnt[i];
);
showData;

過了幾天~想一想還是做一個醜醜的排序好了~排序要在取值前先排。原理用最簡單的把資料拿起來一個一個排就是了,反正就賭他項目不會很多~
PS:有發現他的中文的順序和DB的算法不一樣~但是看起來還是有照筆畫就算了@@...反正之後有被念再說|||Orz...
雖說function裡有用MID(substring)來取第一個字排,不過偶發現跟用AscW來取的話~好像都一樣?!好像都一樣只會回傳第一個字的碼,就總之有被念後再想辦法改好了=_=|||...
@sum_sort
Shared stringVar array all_name;
Shared NumberVar array all_cnt;
Shared NumberVar idx;
Shared stringVar array sort;

NumberVar i;
NumberVar j;
NumberVar k;
StringVar output;
StringVar tmp;
sort := all_name;
for j := 1 to idx do (
    for i := 1 to j do (
        if (MID(sort[j],1) < MID(sort[i],1)) then (
            tmp := sort[j];
            for k := j to i+1 step -1 do (
                sort[k] := sort[k-1];
            );
            sort[i] := tmp;
            exit for;
        );
    );
);

for i := 1 to idx do (
    output := output+sort[i]+',';
);
output;

在排序後要取值的話~先比對有值後再顯示~
@sum_show
Shared stringVar array all_name;
Shared NumberVar array all_cnt;
Shared stringVar array sort;
Shared NumberVar idx;

stringVar showData := '';
NumberVar i;
NumberVar j;
for i := 1 to idx do (
    for j := 1 to idx do (
        if sort[i] = all_name[j] then (
            showData := showData +Chr(13)+Chr(10)+ all_name[j]+':'+ToText(all_cnt[j],'##,###,###,##0',0);
            exit for;
        );
    );  
);
showData;

Crystal Report-把goupName放在一起顯示

首先~讓我嘆N口氣後再說~水晶報表真是一個無言的產品...

它把grouping的東西當迴圈的概念做進來,把RAW data用resultSet一樣只能next的一筆筆抓偶也接受,是還好沒什麼意見,但是在報表中,底下的小統計分析明明就是很常見的放置做法~口訴它居然沒有提供直接的方式來做就是讓人覺得不爽~

然後偶同事跟我說再去DB做個subquery...這個解法也真是挺爛的~因為這個小型統計硬要query的話~要查個N百次才查得完~而且他一次又要撈很多表一定超慢的~要是可以在Java的作法上,只要把RAW Data丟進Map/SortedMap做分類~ 再撈出來統計數字~用用memory就可以解決的小問題(因為這個list量也還好一般,不會讓list的size爆掉,只是它join不少的table和長長一大串的where而已~)~

總之~因為在google上一時找不到解法,所以就想了個很爛的方式解套先Orz...
先以sql的概念來說~其實不過就是想實現
select count(AAA), AAA from tbl_XXX
group by AAA
這麼簡單的問題><~但是為啥會搞得這麼複雜呢!?

報表的設計配置示意如下:

群組首#1          (隱藏但可計)@group_reset
    群組首#2      (隱藏但可計)
        細目          (顯示)
    群組尾#2      (隱藏但可計) #group_count   @group_set
群組尾#1         (顯示) @group_show_name   @group_show_cnt

先設定累計項目(#group_count)為,計算群組2的項目筆數,記得要在群組變更時reset,就可以得到 該首#2的個數累計。

其實若把隱藏的都show出來,就可以得到對的數據了(只是排版不一樣Orz...為啥咪只是想把原本在"群組尾#2"的項目全部擺在同一區會這麼血淚...)~
因為user是想要先看完明細後才看小計~為了這個排版搞死人一_一"...

報表的邏輯~我看是像resultset next的作法一樣,跑過就不見~擺在那就只能看到最後一筆,所以乾脆做變數來把他都先接起來,然後最後再一起顯示~

變數採用名稱跟數量分開~是為了結果排版好看啦XD...
還有要記得一起換行(文字模式用 Chr(13)+Chr(10) )!!這樣才會對齊~
ToText({#group_count}, 0)-->統計會自帶小數點,讓它變整數~

@group_reset 清空變數
Shared stringVar groupDisplay := '';
Shared stringVar groupDisplayCnt := '';

@group_set 把資訊保存
Shared stringVar groupDisplay;
Shared stringVar groupDisplayCnt;

groupDisplay := groupDisplay +Chr(13)+Chr(10)+ GroupName ({@XXX})  ;
groupDisplayCnt := groupDisplayCnt +Chr(13)+Chr(10)+ ToText({#group_count}, 0) ;

@group_show_name
Shared stringVar groupDisplay;

stringVar output := groupDisplay;
output;

2014年8月11日 星期一

html置中排版~

之前好像有寫過~不過最近有找到一個更漂亮的寫法,就記錄一下~

這次大多練習用css排,有單獨使用小調整的才寫在code裡,
規劃上就是以body, div來置中,div則走list區塊的方式在排,若是一個區塊裡又分左右的話,再考慮在div裡面開table或是div/span來處理。總之由上往下整體上都是一條條的div,這樣就不會亂跑了!

內容上主要分 h1(大標), h2(副標), .content(內文文字)
因為上次聽說search上h1會比較容易被查到,所以就配合一下。
.content 主要處裡文字縮排,左右空隙,不過因為margin-right好像很難用沒反應,所以乾脆就用width來算,就齊了一"一b...

這次其實是做的是EDM...(電子DM的版,所以跟網站不太一樣XD)
字大小都僅量用百分比在微調,還蠻好用的!

多個class,指定時用" "(空白隔開就可以了)
<div class="content text_bottom text_grey div_border" >

/**body 和 div要一起配合才會一起置中! div也可以不要用tag, 指定一個包很大的 id元素也可以~*/
body  {
    font-family: Microsoft JhengHei;   /**字型改正黑體*/
    margin: 0;
    text-align: center;
}

div {
    position: relative;
    margin: 0 auto;
    width: 800px;   /**版面寬度,主要配合banner圖*/
    text-align: left;
}


h1  {
    font-size: 32px;
    margin-left: 50px;
}

h2  {
    color:red;
    font-size: 24px;
    margin-left: 50px;
    margin-top: -20px;
}

.content  {
    margin-left: 30px;    /**內文縮排,width為全寬減掉右邊縮排的距離*/
    width: 740px;
}

.text_red  {
    color:red;
}

.text_small  {
    font-size: 70%;    /**字型比率(%),之前比較愛用+1, -1,不過有些微妙變化的字用百分比好用*/
}

.text_bottom  {
    margin:30px;     /**包在.content裡面,就可以縮排兩次了*/
    text-align:center;    /**字的位置,置中*/
}

.text_grey  {
    color:grey;
}



.div_border  {     /**div的外框線,一定要設定border-style才會跑出來*/
    border-color:grey;
    border-size: 1px;
    border-style: solid;
}

其他:
圖片要整張背景圖出來,用height限制最剛好,不會有換行縮排div太多或太少的問題
background-image: url('imgs/edm-07-01.png'); height:500px


unicode string to UTF-8

這是個有點蠢的問題~不過居然一時忘了怎麼解~
卡了兩個小時真是豬頭T_T...

總之~java有個cmd可以把內容轉成unicode--> native2ascii
通常用於語系properties檔內~
然後那個檔裡面的中文字就開始變成\uXXXX的東西~


於是偶就只是想要看一下中文是什麼~然後就卡住了|||Orz...
因為檔案是為UTF-8的編碼~所以讀檔進來就變成一堆英文字~
也當然不能把他用UTF-16讀~因為降子一讀進來的字就錯了~

結果是~請出好用的apache commons lang來一行解決他!
StringEscapeUtils.unescapeJava(str);
就~這樣很簡單的就把檔給寫回原本的中文了T^T...

public void process(String input, String output) throws Exception {
        FileInputStream fis = new FileInputStream(input);
        FileOutputStream fos = new FileOutputStream(output);
       
       
        BufferedReader br = new BufferedReader(new InputStreamReader(fis, "utf-8"));
        while(br.ready())  {
            String row = br.readLine();
            String str = StringEscapeUtils.unescapeJava(row);
//System.out.println("str="+str);           
            str += "\n";
            fos.write(str.getBytes("UTF-8"));
        }
       
        fis.close();
        fos.close();
}


2014年8月7日 星期四

DB2 語法備忘

因為DB2常跟別人的語法不一樣~
備註依有用到時更新~

@@ 限制回傳筆數 ==> limit 3
FETCH FIRST 3 ROWS ONLY

@@ 判斷null時給default值 ==> ISNULL(col, 0)
IFNULL(numcol,0)
IFNULL(strcol,'')

@@動態判斷是否要篩選條件 IFNULL(isNull)
SELECT ... FROM ...
WHERE ...
And STATUS = IFNULL('{?STATUS_P}', STATUS)
 @@CASE 應用
CASE    
     WHEN test-condition THEN value
     WHEN test-condition THEN value
     ELSE value
END    
 
@@ 動態判斷 空值->不篩選, 非空值->條件
常見大家會說用IFNULL來做,但因為用到的地方,它無法傳NULL進來,沒值是傳空字串,所以得用CASE來做
SELECT ... FROM ...
WHERE ...
And STATUS =
CASE
      WHEN '{?STATUS_P}' != '' THEN '{?STATUS_P}'
      ELSE STATUS 
END


@@ 用動態的方式,把三個條件is not null, is null, 不篩選做成一個SQL...
用CASE 來將符合條件的資料給成一個新的欄位 DATA_CHECK,符合給YES,不合給NO,然後再從中挑出DATA_CHECK='YES'的就可以成功了!其中

example:
SELECT a.* FROM (
SELECT XXX.ID, YYY.ID as ID2,
CASE
           WHEN ('{?TYPE_P}'  = '1' And YYY.ID is not Null) THEN 'YES'
           WHEN ('{?TYPE_P}' = '2' And YYY.ID is Null) THEN 'YES'
           WHEN ('{?TYPE_P}' = '0') THEN 'YES'
           ELSE 'NO'
END as DATA_CHECK,
......From XXX, YYY
Where ....
FETCH FIRST 10 ROWS ONLY ) a
WHERE a.DATA_CHECK = 'YES'

2014年7月30日 星期三

postgresql 9.3 筆記-2

Reserved Words
保留字集,建table欄位取名字之前都會先掃一下,以免造成困擾~
http://www.postgresql.org/docs/9.3/static/sql-keywords-appendix.html


Operators
其中比較特別的就是::,pg轉型的寫法
http://www.postgresql.org/docs/9.3/static/sql-syntax-lexical.html#SQL-SYNTAX-OPERATORS

Check Constraints
用於輸入值的檢查,在phppgadmin中,被放在"約束"的項目底下有CHECK
Check要通過(結果要true),或是該資料值是NULL才會過喔~
reference上使用CREATE TABLE的語法說明,是可以指定名稱或沒有名稱,不過透過UI介面,理論上就會有那個名稱了

CREATE TABLE products (
product_no integer,
name text,
price numeric
CHECK (price>0)
);

CREATE TABLE products (
product_no integer,
name text,
price numeric
CONSTRAINT positive_price CHECK (price>0)
);

So, to specify a named constraint, use the key word CONSTRAINT followed by an identifier followed by the constraint definition. (If you don’t specify a constraint name in this way, the system chooses a name for you.)
A check constraint can also refer to several columns. Say you store a regular price and a discounted price, and you want to ensure that the discounted price is lower than the regular price:

CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price>0),
discounted_price numeric CHECK (discounted_price>0),CHECK (price>discounted_price)
);




等同於
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price>0),
discounted_price numeric, CHECK (discounted_price>0 AND price>discounted_price)
);

通過條件說明,null值也會通過,所以若要求非null且check通過的話,要額外設定非NULL的限制(Not-Null Constraints)
It should be noted that a check constraint is satisfied if the check expression evaluates to true or the null value. Since most expressions will evaluate to the null value if any operand is null, they will not prevent null values in the constrained columns. To ensure that a column does not contain null values, the not-null constraint described in the next section can be used.


Foreign Keys
外鍵,是在db裡面還蠻常用到的概念,除了基本的設定之後,外鍵的主要資料一定得先存在才能寫進去~意外發現pg裡面還有代入cascade的概念(第一次知道是從hibernate上得到的),可以自己連動處理資料還蠻方便的,但是rule得先讀懂,已免出現一些讓人傻眼的結果~

CREATE TABLE orders (
order_id integer PRIMARY KEY,
product_no integer REFERENCES products (product_no),
quantity integer
);

Now it is impossible to create orders with non-NULL product_no entries that do not appear in the products table.
We say that in this situation the orders table is the referencing table and the products table is the referenced table. Similarly, there are referencing and referenced columns.

CREATE TABLE order_items (
product_no integer REFERENCES products ON DELETE RESTRICT,
order_id integer REFERENCES orders ON DELETE CASCADE,
quantity integer,
PRIMARY KEY (product_no, order_id)
);

FK的用法
http://www.postgresql.org/docs/9.3/static/sql-createtable.html
FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
    [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE action ] [ ON UPDATE action ]

其中action可以填入
NO ACTION
Produce an error indicating that the deletion or update would create a foreign key constraint violation. If the constraint is deferred, this error will be produced at constraint check time if there still exist any referencing rows. This is the default action.
RESTRICT
Produce an error indicating that the deletion or update would create a foreign key constraint violation. This is the same as NO ACTION except that the check is not deferrable.
CASCADE
Delete any rows referencing the deleted row, or update the values of the referencing column(s) to the new values of the referenced columns, respectively.
SET NULL
Set the referencing column(s) to null.
SET DEFAULT
Set the referencing column(s) to their default values. (There must be a row in the referenced table matching the default values, if they are not null, or the operation will fail.)

Privileges
因為pg內的權限定義到相當的細,所以比較習慣就指定專用的owner來處裡,記得是每個table的每一樣權限都得設定,如果降子就很累了,除非是有限制只開一兩個table使用。其語法主要關鍵字用GRANT/REVOKE。

There are different kinds of privileges:
SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER,CREATE,CONNECT,TEMPORARY,EXECUTE, and USAGE.




Schemas


schema平常看起來是用不到,不過在有管理的需求上還是可以多加利用~

There are several reasons why one might want to use schemas:
•To allow many users to use one database without interfering with each other.
•To organize database objects into logical groups to make them more manageable.
•Third-party applications can be put into separate schemas so they do not collide with the names of other objects.


Partitioning
partition通常是在大量資料的查詢使用上會比較有效果,pg現在也有支援,不過看起來相當的複雜...裡頭寫了一個很大篇幅的文章,等以後真的不幸碰到再來試試吧@@


Foreign Data
這也是一個比較新的名詞,概念是聽來的,大概就是可以外掛一些像csv, 有format txt那樣的檔案資料, 透過下sql來做操作,因為不是真的將資料寫進DB裡,所以聽說可以應用在像log分析之類的東西上~


postgresql 9.3 筆記-1

Having
可用於groupby後再做條件的過濾~

SELECT city, max(temp_lo)
FROM weather
GROUP BY city;

city | max
---------------+-----
Hayward | 37
San Francisco | 46
(2 rows)

which gives us one output row per city. Each aggregate result is computed over the table rows match-ing that city. We can filter these grouped rows using HAVING:
SELECT city, max(temp_lo)
FROM weather
GROUP BY city
HAVING max(temp_lo)<40;

city | max
---------+-----
Hayward | 37
(1 row)


Transactions
這個寫程式就不太會用到~因為程式大多有做transaction,jdbc的autocommit/commit/rollback就是囉~
不過註記一下如果在sql內下的話是~

BEGIN;
UPDATE accounts SET balance = balance - 100.00
WHERE name = ’Alice’;
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
WHERE name = ’Bob’;
-- oops ... forget that and use Wally’s account
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
WHERE name = ’Wally’;
COMMIT;

Inheritance
table繼承,以往得到的概念做法,都還是建兩個table,在程式實做時同時更新或異動,不過發現這裡有一個現成的做法
reference上寫了一個很棒的說明

Inheritance is a concept from object-oriented databases. It opens up interesting new possibilities of database design.
Let’s create two tables: A table cities and a table capitals. Naturally, capitals are also cities, so you want some way to show the capitals implicitly when you list all cities. If you’re really clever you might invent some scheme like this:

CREATE TABLE capitals (
name text,
population real,
altitude int, -- (in ft)
state char(2)
);
CREATE TABLE non_capitals (
name text,
population real,
altitude int -- (in ft)
);
CREATE VIEW cities AS
SELECT name, population, altitude FROM capitals
UNION
SELECT name, population, altitude FROM non_capitals;

This works OK as far as querying goes, but it gets ugly when you need to update several rows, for one thing.
A better solution is this:

CREATE TABLE cities (
name text,
population real,
altitude int -- (in ft)
);
CREATE TABLE capitals (
state char(2)
) INHERITS (cities);

In this case, a row of capitals inherits all columns (name,population, and altitude) from its parent, cities. The type of the column name is text, a native PostgreSQL type for variable length character strings. State capitals have an extra column,state, that shows their state.

In PostgreSQL,a table can inherit from zero or more other tables


postgresql 9.3 筆記-0

這幾年open source開始出現大量的商業化~
偶猜一部份原因也許是被某些公司買走~準備開始進行綁商品賣的方式~

看看走經很多的Java, 從7之後快要變成甲骨文專用sdk了~
好樣的spring綁自家產品還有縮減對其他陣營的支援(spring4+hibernate要漂亮~我看是要榜第三方開發連節方式了~hibernate記得是JBoss那邊的~),現在spring的lib下載點超難找的~

另外也許是因為景氣問題~這些open source沒有金援也要活不下去了...
總之~偶不太喜歡這些走經的open source,但是要求人家做慈善事業是也有點過份~唉唉~

偶愛用低postgresql,其實之前就有企業版的了~叫EDB,不過以前也不會廣告~最近再來看~廣告就大喇喇的擺在旁邊,文件也是用EDB的名稱~就只好說這些是有名open source的命運吧~那天從.org轉成.com,偶想也不該意外了~

最近9.4版出來了~不過可能還在白老鼠~看了版本功能差異
http://www.postgresql.org/about/featurematrix/
後來就先下了9.3來~
(之前最讚賞9.1的是Extension package installation,很好用,不像以前要跑好幾個sql檔,還要懷疑跑失敗的那幾行有沒有關係~囧a~)
主要是看上JSON的處裡,這是個很有前途的部份,偶個人覺得啦~
還有Foreign Tables, Foreign Data處理的部份,雖然以前也沒用過,但聽起來很厲害的樣子~
剛好有點時間,就來讀一下~

windows安裝保持以往快樂安裝包的好習慣~
點兩下裝完後,會問還有沒有要其他的額外工具~
基本的pgAdmin III原本就會裝,但是好用的phppgadmin也千萬別忘掉(因為會順便裝apache server很方便)

身為PostGIS的支持者~有Extension 和win快樂安裝包真是個方便~

至於筆記的來源主要是
http://www.postgresql.org/docs/manuals/

只挑個人覺得要記錄的部份來寫,有些很熟或是覺得沒必要的就跳過~

application server和solaris一些小東西

 備註一下...
這次用到的環境是solaris 10+sun application server

solaris 雖然是liunx, 但是用起來還是有差...

cd , ls 差不多,習慣用-l都還好~
不過在檔案後面會出現*...好像表示非owner的檔案意思或是那是root的檔案~
用root來看就不會有*

在pietty上看起來的差別是~不曉得是不是建的人有設定~還是他就是那樣子?!
root的指令下法好像不能用左右改字~會變特殊符號?
所以只能一個字一個字敲~還好pietty的貼上功能還可以用QQ~
另外root看不到所在路徑(>左邊通常會顯示路徑)~但是會跟su前的那個路徑一樣~
為了確保安全~在執行前都先ls一下~
非root帳號~指令列就會標明完整的路徑~

常用的 tail -n 100 在solaris上要改成
tail -100 server.log

tar上~有些引數不能用~所以後來都用這組(-cf)
tar -cf abc.0728.tar abc/

chmod上改用文字描述(user+write權限)
chmod u+w aaa.txt

hosts位置也有點不同~
/etc/hosts還是有東西~但那是捷徑... ls -l後就可以看到實體位置

sun application server
偶只能說評價差~不過當年同期的TOMCAT可能還在3~5,BUG很慘烈的年代~也許是沒那麼差~不過用過TOMCAT6以後的穩定度~心中有很多os...

 先說中文化的介面真是造成認知上的困擾...

正統布署是用war,在application server的admin GUI上上傳,再指定在那幾個instance(實例),雖然偶覺得有指定了,但是他好像跟本就不鳥,不知道是中文翻議錯誤還是怎樣~
到server下去看source日期有時後跟本沒變~ (可是有時後又有~搭配七月特效,這真的只是古早的靈異問題嗎一.一a)

所以最保險的做法還是布署後,再去各server的實體路徑下確認檔案更換了沒,測試修bug就乾脆去換server下的單檔再重新啟動server就好了~因為佈署非常的慢~

某次悲慘的情境就是同時deploy兩個server...結果不知道布太久斷掉還怎樣,總之在gui上就出現unknow(狀態不明)的情況,此時用gui的任何按鈕就會送你一堆莫名其妙的Exception...
過一天還是一樣~google上沒找到解法,因為真的很難google...名字取太差了,難怪之後改玻離魚還比較好辨識

總之解法就是,在看他的目錄結構的時後,看到server下有個bin,裡面還寫得很好懂的start, stop,鼓起勇氣給它stopserv下去,反正原本也就爛了,再爛也沒差了...stopserv後果然有些不好看的訊息,再去gui上看,狀態終於歸回未啟動!再啟動就正常了^^~

猜測可能是由agent控制server時,可能timeout(因為當時真的等到想關網頁了),或是某些檔案內容鎖檔io問題或是balabala的,變成agent狀態不明無法判別再處裡。因為偶看到的是EOFException,這個通常是搶檔案或是檔案沒寫全的錯誤。

2014年5月20日 星期二

android seekbar custom style

seekbar 好像跟 processbar差不多~

不過seekbar 我覺得是指user input, processbar比較偏系統自動運作(進度)

layer_bar.xml

先決定自定的顏色, 用layer-list製作
background 背景色
secondaryProgress 前景色(bar條顏色)
若是thumb(指標)~ 要用製作的話就在這邊加好像就會出來~
不過我的指標是用自訂圖檔, 就不用再寫~~

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@android:id/background">
    <shape>
        <stroke android:color="#FFBB73"  />
        <solid android:color="#FFBB73"  />
    </shape>
</item>

<item android:id="@android:id/secondaryProgress">
    <clip>
        <shape>
            <stroke android:color="#6DE76E"  />
            <solid android:color="#6DE76E"  />
        </shape>
    </clip>
</item>

</layer-list>

resources xml:

minHeight/maxHeight 可改bar的寬度(高度)
thumb 主要是換中間的圖標(自己加一個png進去)
progressDrawable 是左半邊繪
indeterminateDrawable 算底圖

<style name="tallerBarStyle" parent="@android:style/Widget.SeekBar">
    <item name="android:indeterminateOnly">false</item>
    <item name="android:progressDrawable">@drawable/layer_bar</item>
    <item name="android:indeterminateDrawable">@drawable/layer_bar</item>
    <item name="android:minHeight">8dip</item>
    <item name="android:maxHeight">20dip</item>
    <item name="android:thumb">@drawable/monster09</item>
 </style>

xml:

max 為bar的最高值, 0~android:max

<SeekBar
        android:id="@+id/seekBar1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="111dp"
        android:max="9"
        style="@+style/tallerBarStyle"/>

code:

以上設定應該~ 就可以決定外型了~ 再來就是加bar的listener...
通常就是bar值變動~ 改些資料值~

      bar1.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
            @Override      
            public void onStopTrackingTouch(SeekBar seekBar) {     
                // TODO Auto-generated method stub     
            }      

            @Override      
            public void onStartTrackingTouch(SeekBar seekBar) {    
                // TODO Auto-generated method stub     
            }      

            @Override      
            public void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {    
                // TODO Auto-generated method stub
                showInfo();
            }      
        });

----------------------------------------------------------------------------------
如果只改指標~ 那條BAR不介意細細的~ 有更簡單的寫法~
不用做style...只做簡單顏色可用colorFiliter

Drawable thumb = getResources().getDrawable( R.drawable.monster09 );
bar2.setThumb(thumb);
bar2.getProgressDrawable().setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);


android textview scroll

xml:

<TextView
        android:id="@+id/textView1"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:scrollbarStyle="insideOverlay"
        android:scrollbars="vertical"
        android:text="TextView"
        android:textAlignment="center"
        android:textSize="25sp"
        android:fadeScrollbars="false" />



code:

private TextView tv1= null;
tv1.setMovementMethod(new ScrollingMovementMethod());

2014年3月5日 星期三

spring4 + hibernate4 Java sample code

java code基本上我看跟3是一樣的XD...
就是那個hibernate currentSession怪怪的QQ...

就照基本的spring分吧~
不過偶省掉了一個有些版本在寫的interface/implement...

我通常使用非特定的物件來處理~
因為return資料的型態為json格式, 在Java上不用定死的Bean物件~
轉而利用List, Map來組成~
好處是不用處理特定的型態~ Json物件裡面還是有要分String/int/...bala
取用也要放對才有正確的型態~
不過用基礎物件的話~ 只要放好Integer/Double/String....etc...balabala...
甚至於null, 裡面若還是List/Map 多層也都會正確轉換~
要注意的大概是Date型態~ 自動轉換會變成非常複雜的js Date物件~
所以我大多是在轉入Map時~ 用SimpleDateFormat先轉成需要的字串格式就可以了~

spring協助處理了一些uri pattern的問題~
不過也會因為他包過~ 造成有時候不知道發生了什麼事~
碰過的幾項注意事項大概就是

1. uri pattern的結尾不要輸入有點"."的資料~
bad pattern: /aaa/{num}
因為spring會自己把"."後面的資料切掉~ 對數字來說~ 數字就錯了!!...ex: 11.993 --> 11
避免方式大概就是在會有點的資料多一個/, 或是不要以他為最後一個參數
call use: http://xxxxxxx/aaa/11.993/
或是改pattern為 /aaa/{num}/input

2.  pattern變數有空白
bad pattern: /aaa/{emp ty}/input
空白有時候很小~ 很容易沒看到XD...然後他就莫名的GG了

3. pattern和底下的變數名稱不一樣
copy 來copy去的時候~ 挺容易發生的...

4. 不同method使用到重覆的pattern
這個應該很好理解~ 命名規則最好一開始就能先有個規則~ 以免剛好跟共同開發的人~ 心有靈兮一點通後就打架了...

5. 中文轉碼問題...
因為web server本身就可以設定編碼(若不設定會看local/OS地區, 或你安裝的那一包...有時候外在主機環境~ 是不能改web server設定的~ 就只能從程式動)... 而filiter也可以設定... spring也會有小聰明...連Broswer都很貼心的幫你轉(FF/Chrome會, 老IE不會....)
為了可以write once..run anywhere....別人好心幫你搬家完後也不會出事 ...
最保險的還是前後端講好~ 前端先encodeURIComponent~
這樣子永遠不會被誰多轉一次

sample code
-----------------------------------
Controller
----------------------------------

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.apache.log4j.Logger;

import java.util.*;

@Controller
public class SampleController {

    @Autowired
    SampleService theService;

    @RequestMapping(value={"/test/list"}, method = RequestMethod.GET)
    public @ResponseBody List fn1(Model model){
        return theService.test1();
    }
}

-------------------------------------------
Service
-------------------------------------------
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Component
public class SampleService {

    @Autowired
    SampleDao dao = null;


    public List test1()  {
        return dao.test1();
    }
}

-----------------------------------------
Dao
----------------------------------------
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Component;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.SQLQuery;
import org.hibernate.type.StringType;
import org.hibernate.type.TimestampType;
import org.hibernate.type.IntegerType;

import java.util.*;
import java.text.SimpleDateFormat;
import com.iisi.commons.DBQuickUtils;

@Repository
public class SampleDao {

    Logger logg = Logger.getLogger(this.getClass());

    @Autowired
    SessionFactory sessionFactory;

     SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private Session curSession()  {
        Session ses = sessionFactory.getCurrentSession();
        return ses;
    }

    public List test1()  {

        String sql = "select * from test_table";
        SQLQuery qry = curSession().createSQLQuery(sql);
        qry = qry.addScalar("id", IntegerType.INSTANCE).addScalar("name", StringType.INSTANCE)
                 .addScalar("utime", TimestampType.INSTANCE);

        List lst = qry.list();

        List re = hibernateResultToMap(lst, sf, new String[]{"ID", "NAME", "TIME"});

        return re;
    }
}

spring4 + hibernate4 簡易設定

為了配合公司部門績效~ 老是要搞那些framework...
但是他就是肥滋滋的...只有越來越胖...搞得看起來很複雜~ 好像很厲害的樣子...
但是在偶低眼裡還是不如jdbc...

就又先做個簡單的版本設定吧~
因該案子要做的部分很簡單~ 只要撈DB/中介以REST提供的服務就好~
沒有網頁~沒有SOAP~\(^_^)/~

lib配置就不提囉~ 反正有少放的話~ 會一直有ClassNotFound來煩~ 應該就會知道~

單純設定檔~
先從最基本的入口web.xml開始
1. 加入spring主設定檔(路徑要寫對)
2. 有用到DispatcherServlet處理uri pattern和response
3. 用到OpenSessionInViewFilter, 是因為不知道為啥~ open session有問題~ 查一查~ 好像是說在hibernate4, 搭配spring的實作調用不同了~ 在程式內若要直接取currentSession會有問題~ 但是我又不想在程式內用openSession...怕有人又給我忘了關...所以直接調用hibernate4的 filiter出來處理吧QQ/
(基本上~ 我認為會這樣是...政治問題XD...因為hibernate被JBoss買走~ spring是VMware體系...越來越不Open是可想而之的...不綁綁我家的server和機器怎麼賺錢!!?為了凸顯其他家開發上困難或是慢~ 好在sales時做個勝表...少一些其他支援也是正常...)

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring4/root-context.xml</param-value>
    </context-param>

    <!-- Sets the default profile to use in the absence of any profiles set
        at deployment time. -->
    <context-param>
        <param-name>spring.profile.default</param-name>
        <param-value>dev</param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Handles requests into the application -->  
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring4/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
  
    <filter>
          <filter-name>hibernateFilter</filter-name>
          <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
          <init-param>
             <param-name>sessionFactoryBeanName</param-name>
             <param-value>sessionFactory</param-value>       
          </init-param>    
       </filter>

    <filter-mapping>
        <filter-name>hibernateFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <security-constraint>
        <web-resource-collection>
            <url-pattern>/*</url-pattern>
            <http-method>PUT</http-method>
            <http-method>DELETE</http-method>
            <http-method>HEAD</http-method>
            <http-method>OPTIONS</http-method>
            <http-method>TRACE</http-method>
        </web-resource-collection>
        <auth-constraint></auth-constraint>
    </security-constraint>

接下來~
指向root-context, 我簡化為三個設定
server參數檔--> properties
掃component路徑檔 --> servlet-context
資料來源設定(hibernate) --> data
<!-- Turn on support for @Annotation-based configuration e.g. @Inject -->
    <context:annotation-config />

    <!-- Loads application properties -->
    <import resource="properties.xml" />
   
    <!-- Loads controller/component -->
    <import resource="servlet-context.xml" />
   
    <!-- Loads model(hibernate) -->
    <import resource="data.xml" />


component檔內容
直接給他上層就好~ 省得打一堆資料夾~
<context:component-scan base-package="xxx.yyy.zzz.web.**" />

data檔內容~
差別大概就是~hibernate4 的調用class不同~
另外spring4的使用xml schema也些許不同...不過應該google都能找到ok的範例
另外~ 我沒有使用bean OR mapping...我習慣使用jdbc的sql操作方式(hsql)...
能更彈性的增減物件屬性~ 以及下條件
(誰叫我老是碰到要用db內的function處理資料...hibernate碰到db function..都很難用...)
加上這次專案只有查詢需求~ 沒有新刪修這些transcation鳥事~ 就更不用管他啦^_^Y
<bean id="dataSource"
    class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName">
            <value>com.microsoft.sqlserver.jdbc.SQLServerDriver</value>           
        </property>
        <property name="url">           
            <value>jdbc:sqlserver://.......</value>
        </property>
        <property name="username">
            <value>oooo</value>
        </property>
        <property name="password">
            <value>xxxx</value>           
        </property>
        <property name="timeBetweenEvictionRunsMillis" value="300000" />
        <property name="numTestsPerEvictionRun" value="6" />
        <property name="minEvictableIdleTimeMillis" value="1800000" />
        <property name="initialSize" value="3" />
        <property name="maxActive" value="100" />
        <property name="maxIdle" value="10" />
        <property name="minIdle" value="1"/>
        <property name="maxWait" value="5000" />       
        <property name="poolPreparedStatements" value="true" />
        <property name="maxOpenPreparedStatements" value="100" />
    </bean>
   
     <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
       
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.use_sql_comments">true</prop>
                <prop key="hibernate.format_sql">false</prop>
                <prop key="hibernate.cache.use_second_level_cache">false</prop>               
            </props>
        </property>
    </bean>


servlet檔內容,
主要設定回傳為json格式ResponseBody
 <!-- messageConverters beans -->   
    <beans:bean id="stringHttpMessageConverter"
        class="org.springframework.http.converter.StringHttpMessageConverter">
    </beans:bean>

    <beans:bean id="mappingJacksonHttpMessageConverter"
        class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
    </beans:bean>   

    <!--
        AnnotationMethodHandlerAdapter messageConverters for @ResponseBody
    -->
    <beans:bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <beans:property name="messageConverters">
            <beans:list>
                <beans:ref bean="stringHttpMessageConverter" />
                <beans:ref bean="mappingJacksonHttpMessageConverter" />
            </beans:list>
        </beans:property>
    </beans:bean>

下篇再補java code的部分