You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Table.java 180KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.io.Serializable;
  6. import java.lang.reflect.Method;
  7. import java.util.ArrayList;
  8. import java.util.Collection;
  9. import java.util.Collections;
  10. import java.util.HashMap;
  11. import java.util.HashSet;
  12. import java.util.Iterator;
  13. import java.util.LinkedHashMap;
  14. import java.util.LinkedHashSet;
  15. import java.util.LinkedList;
  16. import java.util.Map;
  17. import java.util.Set;
  18. import java.util.StringTokenizer;
  19. import java.util.logging.Level;
  20. import java.util.logging.Logger;
  21. import com.vaadin.Application;
  22. import com.vaadin.data.Container;
  23. import com.vaadin.data.Item;
  24. import com.vaadin.data.Property;
  25. import com.vaadin.data.util.ContainerOrderedWrapper;
  26. import com.vaadin.data.util.IndexedContainer;
  27. import com.vaadin.data.util.converter.Converter;
  28. import com.vaadin.event.Action;
  29. import com.vaadin.event.Action.Handler;
  30. import com.vaadin.event.DataBoundTransferable;
  31. import com.vaadin.event.ItemClickEvent;
  32. import com.vaadin.event.ItemClickEvent.ItemClickListener;
  33. import com.vaadin.event.ItemClickEvent.ItemClickNotifier;
  34. import com.vaadin.event.MouseEvents.ClickEvent;
  35. import com.vaadin.event.dd.DragAndDropEvent;
  36. import com.vaadin.event.dd.DragSource;
  37. import com.vaadin.event.dd.DropHandler;
  38. import com.vaadin.event.dd.DropTarget;
  39. import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion;
  40. import com.vaadin.terminal.KeyMapper;
  41. import com.vaadin.terminal.LegacyPaint;
  42. import com.vaadin.terminal.PaintException;
  43. import com.vaadin.terminal.PaintTarget;
  44. import com.vaadin.terminal.Resource;
  45. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  46. import com.vaadin.terminal.gwt.client.ui.table.VScrollTable;
  47. /**
  48. * <p>
  49. * <code>Table</code> is used for representing data or components in a pageable
  50. * and selectable table.
  51. * </p>
  52. *
  53. * <p>
  54. * Scalability of the Table is largely dictated by the container. A table does
  55. * not have a limit for the number of items and is just as fast with hundreds of
  56. * thousands of items as with just a few. The current GWT implementation with
  57. * scrolling however limits the number of rows to around 500000, depending on
  58. * the browser and the pixel height of rows.
  59. * </p>
  60. *
  61. * <p>
  62. * Components in a Table will not have their caption nor icon rendered.
  63. * </p>
  64. *
  65. * @author Vaadin Ltd.
  66. * @version
  67. * @VERSION@
  68. * @since 3.0
  69. */
  70. @SuppressWarnings({ "deprecation" })
  71. public class Table extends AbstractSelect implements Action.Container,
  72. Container.Ordered, Container.Sortable, ItemClickNotifier, DragSource,
  73. DropTarget, HasComponents {
  74. private transient Logger logger = null;
  75. /**
  76. * Modes that Table support as drag sourse.
  77. */
  78. public enum TableDragMode {
  79. /**
  80. * Table does not start drag and drop events. HTM5 style events started
  81. * by browser may still happen.
  82. */
  83. NONE,
  84. /**
  85. * Table starts drag with a one row only.
  86. */
  87. ROW,
  88. /**
  89. * Table drags selected rows, if drag starts on a selected rows. Else it
  90. * starts like in ROW mode. Note, that in Transferable there will still
  91. * be only the row on which the drag started, other dragged rows need to
  92. * be checked from the source Table.
  93. */
  94. MULTIROW
  95. }
  96. protected static final int CELL_KEY = 0;
  97. protected static final int CELL_HEADER = 1;
  98. protected static final int CELL_ICON = 2;
  99. protected static final int CELL_ITEMID = 3;
  100. protected static final int CELL_GENERATED_ROW = 4;
  101. protected static final int CELL_FIRSTCOL = 5;
  102. public enum Align {
  103. /**
  104. * Left column alignment. <b>This is the default behaviour. </b>
  105. */
  106. LEFT("b"),
  107. /**
  108. * Center column alignment.
  109. */
  110. CENTER("c"),
  111. /**
  112. * Right column alignment.
  113. */
  114. RIGHT("e");
  115. private String alignment;
  116. private Align(String alignment) {
  117. this.alignment = alignment;
  118. }
  119. @Override
  120. public String toString() {
  121. return alignment;
  122. }
  123. public Align convertStringToAlign(String string) {
  124. if (string == null) {
  125. return null;
  126. }
  127. if (string.equals("b")) {
  128. return Align.LEFT;
  129. } else if (string.equals("c")) {
  130. return Align.CENTER;
  131. } else if (string.equals("e")) {
  132. return Align.RIGHT;
  133. } else {
  134. return null;
  135. }
  136. }
  137. }
  138. /**
  139. * @deprecated from 7.0, use {@link Align#LEFT} instead
  140. */
  141. @Deprecated
  142. public static final Align ALIGN_LEFT = Align.LEFT;
  143. /**
  144. * @deprecated from 7.0, use {@link Align#CENTER} instead
  145. */
  146. @Deprecated
  147. public static final Align ALIGN_CENTER = Align.CENTER;
  148. /**
  149. * @deprecated from 7.0, use {@link Align#RIGHT} instead
  150. */
  151. @Deprecated
  152. public static final Align ALIGN_RIGHT = Align.RIGHT;
  153. public enum ColumnHeaderMode {
  154. /**
  155. * Column headers are hidden.
  156. */
  157. HIDDEN,
  158. /**
  159. * Property ID:s are used as column headers.
  160. */
  161. ID,
  162. /**
  163. * Column headers are explicitly specified with
  164. * {@link #setColumnHeaders(String[])}.
  165. */
  166. EXPLICIT,
  167. /**
  168. * Column headers are explicitly specified with
  169. * {@link #setColumnHeaders(String[])}. If a header is not specified for
  170. * a given property, its property id is used instead.
  171. * <p>
  172. * <b>This is the default behavior. </b>
  173. */
  174. EXPLICIT_DEFAULTS_ID
  175. }
  176. /**
  177. * @deprecated from 7.0, use {@link ColumnHeaderMode#HIDDEN} instead
  178. */
  179. @Deprecated
  180. public static final ColumnHeaderMode COLUMN_HEADER_MODE_HIDDEN = ColumnHeaderMode.HIDDEN;
  181. /**
  182. * @deprecated from 7.0, use {@link ColumnHeaderMode#ID} instead
  183. */
  184. @Deprecated
  185. public static final ColumnHeaderMode COLUMN_HEADER_MODE_ID = ColumnHeaderMode.ID;
  186. /**
  187. * @deprecated from 7.0, use {@link ColumnHeaderMode#EXPLICIT} instead
  188. */
  189. @Deprecated
  190. public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT = ColumnHeaderMode.EXPLICIT;
  191. /**
  192. * @deprecated from 7.0, use {@link ColumnHeaderMode#EXPLICIT_DEFAULTS_ID}
  193. * instead
  194. */
  195. @Deprecated
  196. public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID;
  197. public enum RowHeaderMode {
  198. /**
  199. * Row caption mode: The row headers are hidden. <b>This is the default
  200. * mode. </b>
  201. */
  202. HIDDEN(null),
  203. /**
  204. * Row caption mode: Items Id-objects toString is used as row caption.
  205. */
  206. ID(ItemCaptionMode.ID),
  207. /**
  208. * Row caption mode: Item-objects toString is used as row caption.
  209. */
  210. ITEM(ItemCaptionMode.ITEM),
  211. /**
  212. * Row caption mode: Index of the item is used as item caption. The
  213. * index mode can only be used with the containers implementing the
  214. * {@link com.vaadin.data.Container.Indexed} interface.
  215. */
  216. INDEX(ItemCaptionMode.INDEX),
  217. /**
  218. * Row caption mode: Item captions are explicitly specified, but if the
  219. * caption is missing, the item id objects <code>toString()</code> is
  220. * used instead.
  221. */
  222. EXPLICIT_DEFAULTS_ID(ItemCaptionMode.EXPLICIT_DEFAULTS_ID),
  223. /**
  224. * Row caption mode: Item captions are explicitly specified.
  225. */
  226. EXPLICIT(ItemCaptionMode.EXPLICIT),
  227. /**
  228. * Row caption mode: Only icons are shown, the captions are hidden.
  229. */
  230. ICON_ONLY(ItemCaptionMode.ICON_ONLY),
  231. /**
  232. * Row caption mode: Item captions are read from property specified with
  233. * {@link #setItemCaptionPropertyId(Object)}.
  234. */
  235. PROPERTY(ItemCaptionMode.PROPERTY);
  236. ItemCaptionMode mode;
  237. private RowHeaderMode(ItemCaptionMode mode) {
  238. this.mode = mode;
  239. }
  240. public ItemCaptionMode getItemCaptionMode() {
  241. return mode;
  242. }
  243. }
  244. /**
  245. * @deprecated from 7.0, use {@link RowHeaderMode#HIDDEN} instead
  246. */
  247. @Deprecated
  248. public static final RowHeaderMode ROW_HEADER_MODE_HIDDEN = RowHeaderMode.HIDDEN;
  249. /**
  250. * @deprecated from 7.0, use {@link RowHeaderMode#ID} instead
  251. */
  252. @Deprecated
  253. public static final RowHeaderMode ROW_HEADER_MODE_ID = RowHeaderMode.ID;
  254. /**
  255. * @deprecated from 7.0, use {@link RowHeaderMode#ITEM} instead
  256. */
  257. @Deprecated
  258. public static final RowHeaderMode ROW_HEADER_MODE_ITEM = RowHeaderMode.ITEM;
  259. /**
  260. * @deprecated from 7.0, use {@link RowHeaderMode#INDEX} instead
  261. */
  262. @Deprecated
  263. public static final RowHeaderMode ROW_HEADER_MODE_INDEX = RowHeaderMode.INDEX;
  264. /**
  265. * @deprecated from 7.0, use {@link RowHeaderMode#EXPLICIT_DEFAULTS_ID}
  266. * instead
  267. */
  268. @Deprecated
  269. public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID = RowHeaderMode.EXPLICIT_DEFAULTS_ID;
  270. /**
  271. * @deprecated from 7.0, use {@link RowHeaderMode#EXPLICIT} instead
  272. */
  273. @Deprecated
  274. public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT = RowHeaderMode.EXPLICIT;
  275. /**
  276. * @deprecated from 7.0, use {@link RowHeaderMode#ICON_ONLY} instead
  277. */
  278. @Deprecated
  279. public static final RowHeaderMode ROW_HEADER_MODE_ICON_ONLY = RowHeaderMode.ICON_ONLY;
  280. /**
  281. * @deprecated from 7.0, use {@link RowHeaderMode#PROPERTY} instead
  282. */
  283. @Deprecated
  284. public static final RowHeaderMode ROW_HEADER_MODE_PROPERTY = RowHeaderMode.PROPERTY;
  285. /**
  286. * The default rate that table caches rows for smooth scrolling.
  287. */
  288. private static final double CACHE_RATE_DEFAULT = 2;
  289. private static final String ROW_HEADER_COLUMN_KEY = "0";
  290. private static final Object ROW_HEADER_FAKE_PROPERTY_ID = new UniqueSerializable() {
  291. };
  292. /* Private table extensions to Select */
  293. /**
  294. * True if column collapsing is allowed.
  295. */
  296. private boolean columnCollapsingAllowed = false;
  297. /**
  298. * True if reordering of columns is allowed on the client side.
  299. */
  300. private boolean columnReorderingAllowed = false;
  301. /**
  302. * Keymapper for column ids.
  303. */
  304. private final KeyMapper<Object> columnIdMap = new KeyMapper<Object>();
  305. /**
  306. * Holds visible column propertyIds - in order.
  307. */
  308. private LinkedList<Object> visibleColumns = new LinkedList<Object>();
  309. /**
  310. * Holds noncollapsible columns.
  311. */
  312. private HashSet<Object> noncollapsibleColumns = new HashSet<Object>();
  313. /**
  314. * Holds propertyIds of currently collapsed columns.
  315. */
  316. private final HashSet<Object> collapsedColumns = new HashSet<Object>();
  317. /**
  318. * Holds headers for visible columns (by propertyId).
  319. */
  320. private final HashMap<Object, String> columnHeaders = new HashMap<Object, String>();
  321. /**
  322. * Holds footers for visible columns (by propertyId).
  323. */
  324. private final HashMap<Object, String> columnFooters = new HashMap<Object, String>();
  325. /**
  326. * Holds icons for visible columns (by propertyId).
  327. */
  328. private final HashMap<Object, Resource> columnIcons = new HashMap<Object, Resource>();
  329. /**
  330. * Holds alignments for visible columns (by propertyId).
  331. */
  332. private HashMap<Object, Align> columnAlignments = new HashMap<Object, Align>();
  333. /**
  334. * Holds column widths in pixels (Integer) or expand ratios (Float) for
  335. * visible columns (by propertyId).
  336. */
  337. private final HashMap<Object, Object> columnWidths = new HashMap<Object, Object>();
  338. /**
  339. * Holds column generators
  340. */
  341. private final HashMap<Object, ColumnGenerator> columnGenerators = new LinkedHashMap<Object, ColumnGenerator>();
  342. /**
  343. * Holds value of property pageLength. 0 disables paging.
  344. */
  345. private int pageLength = 15;
  346. /**
  347. * Id the first item on the current page.
  348. */
  349. private Object currentPageFirstItemId = null;
  350. /**
  351. * Index of the first item on the current page.
  352. */
  353. private int currentPageFirstItemIndex = 0;
  354. /**
  355. * Holds value of property selectable.
  356. */
  357. private boolean selectable = false;
  358. /**
  359. * Holds value of property columnHeaderMode.
  360. */
  361. private ColumnHeaderMode columnHeaderMode = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID;
  362. /**
  363. * Holds value of property rowHeaderMode.
  364. */
  365. private RowHeaderMode rowHeaderMode = RowHeaderMode.EXPLICIT_DEFAULTS_ID;
  366. /**
  367. * Should the Table footer be visible?
  368. */
  369. private boolean columnFootersVisible = false;
  370. /**
  371. * Page contents buffer used in buffered mode.
  372. */
  373. private Object[][] pageBuffer = null;
  374. /**
  375. * Set of properties listened - the list is kept to release the listeners
  376. * later.
  377. */
  378. private HashSet<Property<?>> listenedProperties = null;
  379. /**
  380. * Set of visible components - the is used for needsRepaint calculation.
  381. */
  382. private HashSet<Component> visibleComponents = null;
  383. /**
  384. * List of action handlers.
  385. */
  386. private LinkedList<Handler> actionHandlers = null;
  387. /**
  388. * Action mapper.
  389. */
  390. private KeyMapper<Action> actionMapper = null;
  391. /**
  392. * Table cell editor factory.
  393. */
  394. private TableFieldFactory fieldFactory = DefaultFieldFactory.get();
  395. /**
  396. * Is table editable.
  397. */
  398. private boolean editable = false;
  399. /**
  400. * Current sorting direction.
  401. */
  402. private boolean sortAscending = true;
  403. /**
  404. * Currently table is sorted on this propertyId.
  405. */
  406. private Object sortContainerPropertyId = null;
  407. /**
  408. * Is table sorting disabled alltogether; even if some of the properties
  409. * would be sortable.
  410. */
  411. private boolean sortDisabled = false;
  412. /**
  413. * Number of rows explicitly requested by the client to be painted on next
  414. * paint. This is -1 if no request by the client is made. Painting the
  415. * component will automatically reset this to -1.
  416. */
  417. private int reqRowsToPaint = -1;
  418. /**
  419. * Index of the first rows explicitly requested by the client to be painted.
  420. * This is -1 if no request by the client is made. Painting the component
  421. * will automatically reset this to -1.
  422. */
  423. private int reqFirstRowToPaint = -1;
  424. private int firstToBeRenderedInClient = -1;
  425. private int lastToBeRenderedInClient = -1;
  426. private boolean isContentRefreshesEnabled = true;
  427. private int pageBufferFirstIndex;
  428. private boolean containerChangeToBeRendered = false;
  429. /**
  430. * Table cell specific style generator
  431. */
  432. private CellStyleGenerator cellStyleGenerator = null;
  433. /**
  434. * Table cell specific tooltip generator
  435. */
  436. private ItemDescriptionGenerator itemDescriptionGenerator;
  437. /*
  438. * EXPERIMENTAL feature: will tell the client to re-calculate column widths
  439. * if set to true. Currently no setter: extend to enable.
  440. */
  441. protected boolean alwaysRecalculateColumnWidths = false;
  442. private double cacheRate = CACHE_RATE_DEFAULT;
  443. private TableDragMode dragMode = TableDragMode.NONE;
  444. private DropHandler dropHandler;
  445. private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT;
  446. private boolean rowCacheInvalidated;
  447. private RowGenerator rowGenerator = null;
  448. private final Map<Field<?>, Property<?>> associatedProperties = new HashMap<Field<?>, Property<?>>();
  449. private boolean painted = false;
  450. private HashMap<Object, Converter<String, Object>> propertyValueConverters = new HashMap<Object, Converter<String, Object>>();
  451. /* Table constructors */
  452. /**
  453. * Creates a new empty table.
  454. */
  455. public Table() {
  456. setRowHeaderMode(ROW_HEADER_MODE_HIDDEN);
  457. }
  458. /**
  459. * Creates a new empty table with caption.
  460. *
  461. * @param caption
  462. */
  463. public Table(String caption) {
  464. this();
  465. setCaption(caption);
  466. }
  467. /**
  468. * Creates a new table with caption and connect it to a Container.
  469. *
  470. * @param caption
  471. * @param dataSource
  472. */
  473. public Table(String caption, Container dataSource) {
  474. this();
  475. setCaption(caption);
  476. setContainerDataSource(dataSource);
  477. }
  478. /* Table functionality */
  479. /**
  480. * Gets the array of visible column id:s, including generated columns.
  481. *
  482. * <p>
  483. * The columns are show in the order of their appearance in this array.
  484. * </p>
  485. *
  486. * @return an array of currently visible propertyIds and generated column
  487. * ids.
  488. */
  489. public Object[] getVisibleColumns() {
  490. if (visibleColumns == null) {
  491. return null;
  492. }
  493. return visibleColumns.toArray();
  494. }
  495. /**
  496. * Sets the array of visible column property id:s.
  497. *
  498. * <p>
  499. * The columns are show in the order of their appearance in this array.
  500. * </p>
  501. *
  502. * @param visibleColumns
  503. * the Array of shown property id:s.
  504. */
  505. public void setVisibleColumns(Object[] visibleColumns) {
  506. // Visible columns must exist
  507. if (visibleColumns == null) {
  508. throw new NullPointerException(
  509. "Can not set visible columns to null value");
  510. }
  511. // TODO add error check that no duplicate identifiers exist
  512. // Checks that the new visible columns contains no nulls and properties
  513. // exist
  514. final Collection<?> properties = getContainerPropertyIds();
  515. for (int i = 0; i < visibleColumns.length; i++) {
  516. if (visibleColumns[i] == null) {
  517. throw new NullPointerException("Ids must be non-nulls");
  518. } else if (!properties.contains(visibleColumns[i])
  519. && !columnGenerators.containsKey(visibleColumns[i])) {
  520. throw new IllegalArgumentException(
  521. "Ids must exist in the Container or as a generated column , missing id: "
  522. + visibleColumns[i]);
  523. }
  524. }
  525. // If this is called before the constructor is finished, it might be
  526. // uninitialized
  527. final LinkedList<Object> newVC = new LinkedList<Object>();
  528. for (int i = 0; i < visibleColumns.length; i++) {
  529. newVC.add(visibleColumns[i]);
  530. }
  531. // Removes alignments, icons and headers from hidden columns
  532. if (this.visibleColumns != null) {
  533. boolean disabledHere = disableContentRefreshing();
  534. try {
  535. for (final Iterator<Object> i = this.visibleColumns.iterator(); i
  536. .hasNext();) {
  537. final Object col = i.next();
  538. if (!newVC.contains(col)) {
  539. setColumnHeader(col, null);
  540. setColumnAlignment(col, (Align) null);
  541. setColumnIcon(col, null);
  542. }
  543. }
  544. } finally {
  545. if (disabledHere) {
  546. enableContentRefreshing(false);
  547. }
  548. }
  549. }
  550. this.visibleColumns = newVC;
  551. // Assures visual refresh
  552. refreshRowCache();
  553. }
  554. /**
  555. * Gets the headers of the columns.
  556. *
  557. * <p>
  558. * The headers match the property id:s given my the set visible column
  559. * headers. The table must be set in either
  560. * {@link #COLUMN_HEADER_MODE_EXPLICIT} or
  561. * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the
  562. * headers. In the defaults mode any nulls in the headers array are replaced
  563. * with id.toString().
  564. * </p>
  565. *
  566. * @return the Array of column headers.
  567. */
  568. public String[] getColumnHeaders() {
  569. if (columnHeaders == null) {
  570. return null;
  571. }
  572. final String[] headers = new String[visibleColumns.size()];
  573. int i = 0;
  574. for (final Iterator<Object> it = visibleColumns.iterator(); it
  575. .hasNext(); i++) {
  576. headers[i] = getColumnHeader(it.next());
  577. }
  578. return headers;
  579. }
  580. /**
  581. * Sets the headers of the columns.
  582. *
  583. * <p>
  584. * The headers match the property id:s given my the set visible column
  585. * headers. The table must be set in either
  586. * {@link #COLUMN_HEADER_MODE_EXPLICIT} or
  587. * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the
  588. * headers. In the defaults mode any nulls in the headers array are replaced
  589. * with id.toString() outputs when rendering.
  590. * </p>
  591. *
  592. * @param columnHeaders
  593. * the Array of column headers that match the
  594. * {@link #getVisibleColumns()} method.
  595. */
  596. public void setColumnHeaders(String[] columnHeaders) {
  597. if (columnHeaders.length != visibleColumns.size()) {
  598. throw new IllegalArgumentException(
  599. "The length of the headers array must match the number of visible columns");
  600. }
  601. this.columnHeaders.clear();
  602. int i = 0;
  603. for (final Iterator<Object> it = visibleColumns.iterator(); it
  604. .hasNext() && i < columnHeaders.length; i++) {
  605. this.columnHeaders.put(it.next(), columnHeaders[i]);
  606. }
  607. requestRepaint();
  608. }
  609. /**
  610. * Gets the icons of the columns.
  611. *
  612. * <p>
  613. * The icons in headers match the property id:s given my the set visible
  614. * column headers. The table must be set in either
  615. * {@link #COLUMN_HEADER_MODE_EXPLICIT} or
  616. * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers
  617. * with icons.
  618. * </p>
  619. *
  620. * @return the Array of icons that match the {@link #getVisibleColumns()}.
  621. */
  622. public Resource[] getColumnIcons() {
  623. if (columnIcons == null) {
  624. return null;
  625. }
  626. final Resource[] icons = new Resource[visibleColumns.size()];
  627. int i = 0;
  628. for (final Iterator<Object> it = visibleColumns.iterator(); it
  629. .hasNext(); i++) {
  630. icons[i] = columnIcons.get(it.next());
  631. }
  632. return icons;
  633. }
  634. /**
  635. * Sets the icons of the columns.
  636. *
  637. * <p>
  638. * The icons in headers match the property id:s given my the set visible
  639. * column headers. The table must be set in either
  640. * {@link #COLUMN_HEADER_MODE_EXPLICIT} or
  641. * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers
  642. * with icons.
  643. * </p>
  644. *
  645. * @param columnIcons
  646. * the Array of icons that match the {@link #getVisibleColumns()}
  647. * .
  648. */
  649. public void setColumnIcons(Resource[] columnIcons) {
  650. if (columnIcons.length != visibleColumns.size()) {
  651. throw new IllegalArgumentException(
  652. "The length of the icons array must match the number of visible columns");
  653. }
  654. this.columnIcons.clear();
  655. int i = 0;
  656. for (final Iterator<Object> it = visibleColumns.iterator(); it
  657. .hasNext() && i < columnIcons.length; i++) {
  658. this.columnIcons.put(it.next(), columnIcons[i]);
  659. }
  660. requestRepaint();
  661. }
  662. /**
  663. * Gets the array of column alignments.
  664. *
  665. * <p>
  666. * The items in the array must match the properties identified by
  667. * {@link #getVisibleColumns()}. The possible values for the alignments
  668. * include:
  669. * <ul>
  670. * <li>{@link Align#LEFT}: Left alignment</li>
  671. * <li>{@link Align#CENTER}: Centered</li>
  672. * <li>{@link Align#RIGHT}: Right alignment</li>
  673. * </ul>
  674. * The alignments default to {@link Align#LEFT}: any null values are
  675. * rendered as align lefts.
  676. * </p>
  677. *
  678. * @return the Column alignments array.
  679. */
  680. public Align[] getColumnAlignments() {
  681. if (columnAlignments == null) {
  682. return null;
  683. }
  684. final Align[] alignments = new Align[visibleColumns.size()];
  685. int i = 0;
  686. for (final Iterator<Object> it = visibleColumns.iterator(); it
  687. .hasNext(); i++) {
  688. alignments[i] = getColumnAlignment(it.next());
  689. }
  690. return alignments;
  691. }
  692. /**
  693. * Sets the column alignments.
  694. *
  695. * <p>
  696. * The amount of items in the array must match the amount of properties
  697. * identified by {@link #getVisibleColumns()}. The possible values for the
  698. * alignments include:
  699. * <ul>
  700. * <li>{@link Align#LEFT}: Left alignment</li>
  701. * <li>{@link Align#CENTER}: Centered</li>
  702. * <li>{@link Align#RIGHT}: Right alignment</li>
  703. * </ul>
  704. * The alignments default to {@link Align#LEFT}
  705. * </p>
  706. *
  707. * @param columnAlignments
  708. * the Column alignments array.
  709. */
  710. public void setColumnAlignments(Align... columnAlignments) {
  711. if (columnAlignments.length != visibleColumns.size()) {
  712. throw new IllegalArgumentException(
  713. "The length of the alignments array must match the number of visible columns");
  714. }
  715. // Resets the alignments
  716. final HashMap<Object, Align> newCA = new HashMap<Object, Align>();
  717. int i = 0;
  718. for (final Iterator<Object> it = visibleColumns.iterator(); it
  719. .hasNext() && i < columnAlignments.length; i++) {
  720. newCA.put(it.next(), columnAlignments[i]);
  721. }
  722. this.columnAlignments = newCA;
  723. // Assures the visual refresh. No need to reset the page buffer before
  724. // as the content has not changed, only the alignments.
  725. refreshRenderedCells();
  726. }
  727. /**
  728. * Sets columns width (in pixels). Theme may not necessary respect very
  729. * small or very big values. Setting width to -1 (default) means that theme
  730. * will make decision of width.
  731. *
  732. * <p>
  733. * Column can either have a fixed width or expand ratio. The latter one set
  734. * is used. See @link {@link #setColumnExpandRatio(Object, float)}.
  735. *
  736. * @param propertyId
  737. * colunmns property id
  738. * @param width
  739. * width to be reserved for colunmns content
  740. * @since 4.0.3
  741. */
  742. public void setColumnWidth(Object propertyId, int width) {
  743. if (propertyId == null) {
  744. // Since propertyId is null, this is the row header. Use the magic
  745. // id to store the width of the row header.
  746. propertyId = ROW_HEADER_FAKE_PROPERTY_ID;
  747. }
  748. if (width < 0) {
  749. columnWidths.remove(propertyId);
  750. } else {
  751. columnWidths.put(propertyId, Integer.valueOf(width));
  752. }
  753. requestRepaint();
  754. }
  755. /**
  756. * Sets the column expand ratio for given column.
  757. * <p>
  758. * Expand ratios can be defined to customize the way how excess space is
  759. * divided among columns. Table can have excess space if it has its width
  760. * defined and there is horizontally more space than columns consume
  761. * naturally. Excess space is the space that is not used by columns with
  762. * explicit width (see {@link #setColumnWidth(Object, int)}) or with natural
  763. * width (no width nor expand ratio).
  764. *
  765. * <p>
  766. * By default (without expand ratios) the excess space is divided
  767. * proportionally to columns natural widths.
  768. *
  769. * <p>
  770. * Only expand ratios of visible columns are used in final calculations.
  771. *
  772. * <p>
  773. * Column can either have a fixed width or expand ratio. The latter one set
  774. * is used.
  775. *
  776. * <p>
  777. * A column with expand ratio is considered to be minimum width by default
  778. * (if no excess space exists). The minimum width is defined by terminal
  779. * implementation.
  780. *
  781. * <p>
  782. * If terminal implementation supports re-sizable columns the column becomes
  783. * fixed width column if users resizes the column.
  784. *
  785. * @param propertyId
  786. * columns property id
  787. * @param expandRatio
  788. * the expandRatio used to divide excess space for this column
  789. */
  790. public void setColumnExpandRatio(Object propertyId, float expandRatio) {
  791. if (expandRatio < 0) {
  792. columnWidths.remove(propertyId);
  793. } else {
  794. columnWidths.put(propertyId, new Float(expandRatio));
  795. }
  796. }
  797. public float getColumnExpandRatio(Object propertyId) {
  798. final Object width = columnWidths.get(propertyId);
  799. if (width == null || !(width instanceof Float)) {
  800. return -1;
  801. }
  802. final Float value = (Float) width;
  803. return value.floatValue();
  804. }
  805. /**
  806. * Gets the pixel width of column
  807. *
  808. * @param propertyId
  809. * @return width of column or -1 when value not set
  810. */
  811. public int getColumnWidth(Object propertyId) {
  812. if (propertyId == null) {
  813. // Since propertyId is null, this is the row header. Use the magic
  814. // id to retrieve the width of the row header.
  815. propertyId = ROW_HEADER_FAKE_PROPERTY_ID;
  816. }
  817. final Object width = columnWidths.get(propertyId);
  818. if (width == null || !(width instanceof Integer)) {
  819. return -1;
  820. }
  821. final Integer value = (Integer) width;
  822. return value.intValue();
  823. }
  824. /**
  825. * Gets the page length.
  826. *
  827. * <p>
  828. * Setting page length 0 disables paging.
  829. * </p>
  830. *
  831. * @return the Length of one page.
  832. */
  833. public int getPageLength() {
  834. return pageLength;
  835. }
  836. /**
  837. * Sets the page length.
  838. *
  839. * <p>
  840. * Setting page length 0 disables paging. The page length defaults to 15.
  841. * </p>
  842. *
  843. * <p>
  844. * If Table has width set ({@link #setWidth(float, int)} ) the client side
  845. * may update the page length automatically the correct value.
  846. * </p>
  847. *
  848. * @param pageLength
  849. * the length of one page.
  850. */
  851. public void setPageLength(int pageLength) {
  852. if (pageLength >= 0 && this.pageLength != pageLength) {
  853. this.pageLength = pageLength;
  854. // Assures the visual refresh
  855. refreshRowCache();
  856. }
  857. }
  858. /**
  859. * This method adjusts a possible caching mechanism of table implementation.
  860. *
  861. * <p>
  862. * Table component may fetch and render some rows outside visible area. With
  863. * complex tables (for example containing layouts and components), the
  864. * client side may become unresponsive. Setting the value lower, UI will
  865. * become more responsive. With higher values scrolling in client will hit
  866. * server less frequently.
  867. *
  868. * <p>
  869. * The amount of cached rows will be cacheRate multiplied with pageLength (
  870. * {@link #setPageLength(int)} both below and above visible area..
  871. *
  872. * @param cacheRate
  873. * a value over 0 (fastest rendering time). Higher value will
  874. * cache more rows on server (smoother scrolling). Default value
  875. * is 2.
  876. */
  877. public void setCacheRate(double cacheRate) {
  878. if (cacheRate < 0) {
  879. throw new IllegalArgumentException(
  880. "cacheRate cannot be less than zero");
  881. }
  882. if (this.cacheRate != cacheRate) {
  883. this.cacheRate = cacheRate;
  884. requestRepaint();
  885. }
  886. }
  887. /**
  888. * @see #setCacheRate(double)
  889. *
  890. * @return the current cache rate value
  891. */
  892. public double getCacheRate() {
  893. return cacheRate;
  894. }
  895. /**
  896. * Getter for property currentPageFirstItem.
  897. *
  898. * @return the Value of property currentPageFirstItem.
  899. */
  900. public Object getCurrentPageFirstItemId() {
  901. // Priorise index over id if indexes are supported
  902. if (items instanceof Container.Indexed) {
  903. final int index = getCurrentPageFirstItemIndex();
  904. Object id = null;
  905. if (index >= 0 && index < size()) {
  906. id = getIdByIndex(index);
  907. }
  908. if (id != null && !id.equals(currentPageFirstItemId)) {
  909. currentPageFirstItemId = id;
  910. }
  911. }
  912. // If there is no item id at all, use the first one
  913. if (currentPageFirstItemId == null) {
  914. currentPageFirstItemId = firstItemId();
  915. }
  916. return currentPageFirstItemId;
  917. }
  918. protected Object getIdByIndex(int index) {
  919. return ((Container.Indexed) items).getIdByIndex(index);
  920. }
  921. /**
  922. * Setter for property currentPageFirstItemId.
  923. *
  924. * @param currentPageFirstItemId
  925. * the New value of property currentPageFirstItemId.
  926. */
  927. public void setCurrentPageFirstItemId(Object currentPageFirstItemId) {
  928. // Gets the corresponding index
  929. int index = -1;
  930. if (items instanceof Container.Indexed) {
  931. index = indexOfId(currentPageFirstItemId);
  932. } else {
  933. // If the table item container does not have index, we have to
  934. // calculates the index by hand
  935. Object id = firstItemId();
  936. while (id != null && !id.equals(currentPageFirstItemId)) {
  937. index++;
  938. id = nextItemId(id);
  939. }
  940. if (id == null) {
  941. index = -1;
  942. }
  943. }
  944. // If the search for item index was successful
  945. if (index >= 0) {
  946. /*
  947. * The table is not capable of displaying an item in the container
  948. * as the first if there are not enough items following the selected
  949. * item so the whole table (pagelength) is filled.
  950. */
  951. int maxIndex = size() - pageLength;
  952. if (maxIndex < 0) {
  953. maxIndex = 0;
  954. }
  955. if (index > maxIndex) {
  956. // Note that we pass index, not maxIndex, letting
  957. // setCurrentPageFirstItemIndex handle the situation.
  958. setCurrentPageFirstItemIndex(index);
  959. return;
  960. }
  961. this.currentPageFirstItemId = currentPageFirstItemId;
  962. currentPageFirstItemIndex = index;
  963. }
  964. // Assures the visual refresh
  965. refreshRowCache();
  966. }
  967. protected int indexOfId(Object itemId) {
  968. return ((Container.Indexed) items).indexOfId(itemId);
  969. }
  970. /**
  971. * Gets the icon Resource for the specified column.
  972. *
  973. * @param propertyId
  974. * the propertyId indentifying the column.
  975. * @return the icon for the specified column; null if the column has no icon
  976. * set, or if the column is not visible.
  977. */
  978. public Resource getColumnIcon(Object propertyId) {
  979. return columnIcons.get(propertyId);
  980. }
  981. /**
  982. * Sets the icon Resource for the specified column.
  983. * <p>
  984. * Throws IllegalArgumentException if the specified column is not visible.
  985. * </p>
  986. *
  987. * @param propertyId
  988. * the propertyId identifying the column.
  989. * @param icon
  990. * the icon Resource to set.
  991. */
  992. public void setColumnIcon(Object propertyId, Resource icon) {
  993. if (icon == null) {
  994. columnIcons.remove(propertyId);
  995. } else {
  996. columnIcons.put(propertyId, icon);
  997. }
  998. requestRepaint();
  999. }
  1000. /**
  1001. * Gets the header for the specified column.
  1002. *
  1003. * @param propertyId
  1004. * the propertyId identifying the column.
  1005. * @return the header for the specified column if it has one.
  1006. */
  1007. public String getColumnHeader(Object propertyId) {
  1008. if (getColumnHeaderMode() == ColumnHeaderMode.HIDDEN) {
  1009. return null;
  1010. }
  1011. String header = columnHeaders.get(propertyId);
  1012. if ((header == null && getColumnHeaderMode() == ColumnHeaderMode.EXPLICIT_DEFAULTS_ID)
  1013. || getColumnHeaderMode() == ColumnHeaderMode.ID) {
  1014. header = propertyId.toString();
  1015. }
  1016. return header;
  1017. }
  1018. /**
  1019. * Sets the column header for the specified column;
  1020. *
  1021. * @param propertyId
  1022. * the propertyId identifying the column.
  1023. * @param header
  1024. * the header to set.
  1025. */
  1026. public void setColumnHeader(Object propertyId, String header) {
  1027. if (header == null) {
  1028. columnHeaders.remove(propertyId);
  1029. } else {
  1030. columnHeaders.put(propertyId, header);
  1031. }
  1032. requestRepaint();
  1033. }
  1034. /**
  1035. * Gets the specified column's alignment.
  1036. *
  1037. * @param propertyId
  1038. * the propertyID identifying the column.
  1039. * @return the specified column's alignment if it as one; null otherwise.
  1040. */
  1041. public Align getColumnAlignment(Object propertyId) {
  1042. final Align a = columnAlignments.get(propertyId);
  1043. return a == null ? Align.LEFT : a;
  1044. }
  1045. /**
  1046. * Sets the specified column's alignment.
  1047. *
  1048. * <p>
  1049. * Throws IllegalArgumentException if the alignment is not one of the
  1050. * following: {@link Align#LEFT}, {@link Align#CENTER} or
  1051. * {@link Align#RIGHT}
  1052. * </p>
  1053. *
  1054. * @param propertyId
  1055. * the propertyID identifying the column.
  1056. * @param alignment
  1057. * the desired alignment.
  1058. */
  1059. public void setColumnAlignment(Object propertyId, Align alignment) {
  1060. if (alignment == null || alignment == Align.LEFT) {
  1061. columnAlignments.remove(propertyId);
  1062. } else {
  1063. columnAlignments.put(propertyId, alignment);
  1064. }
  1065. // Assures the visual refresh. No need to reset the page buffer before
  1066. // as the content has not changed, only the alignments.
  1067. refreshRenderedCells();
  1068. }
  1069. /**
  1070. * Checks if the specified column is collapsed.
  1071. *
  1072. * @param propertyId
  1073. * the propertyID identifying the column.
  1074. * @return true if the column is collapsed; false otherwise;
  1075. */
  1076. public boolean isColumnCollapsed(Object propertyId) {
  1077. return collapsedColumns != null
  1078. && collapsedColumns.contains(propertyId);
  1079. }
  1080. /**
  1081. * Sets whether the specified column is collapsed or not.
  1082. *
  1083. *
  1084. * @param propertyId
  1085. * the propertyID identifying the column.
  1086. * @param collapsed
  1087. * the desired collapsedness.
  1088. * @throws IllegalStateException
  1089. * if column collapsing is not allowed
  1090. */
  1091. public void setColumnCollapsed(Object propertyId, boolean collapsed)
  1092. throws IllegalStateException {
  1093. if (!isColumnCollapsingAllowed()) {
  1094. throw new IllegalStateException("Column collapsing not allowed!");
  1095. }
  1096. if (collapsed && noncollapsibleColumns.contains(propertyId)) {
  1097. throw new IllegalStateException("The column is noncollapsible!");
  1098. }
  1099. if (collapsed) {
  1100. collapsedColumns.add(propertyId);
  1101. } else {
  1102. collapsedColumns.remove(propertyId);
  1103. }
  1104. // Assures the visual refresh
  1105. refreshRowCache();
  1106. }
  1107. /**
  1108. * Checks if column collapsing is allowed.
  1109. *
  1110. * @return true if columns can be collapsed; false otherwise.
  1111. */
  1112. public boolean isColumnCollapsingAllowed() {
  1113. return columnCollapsingAllowed;
  1114. }
  1115. /**
  1116. * Sets whether column collapsing is allowed or not.
  1117. *
  1118. * @param collapsingAllowed
  1119. * specifies whether column collapsing is allowed.
  1120. */
  1121. public void setColumnCollapsingAllowed(boolean collapsingAllowed) {
  1122. columnCollapsingAllowed = collapsingAllowed;
  1123. if (!collapsingAllowed) {
  1124. collapsedColumns.clear();
  1125. }
  1126. // Assures the visual refresh. No need to reset the page buffer before
  1127. // as the content has not changed, only the alignments.
  1128. refreshRenderedCells();
  1129. }
  1130. /**
  1131. * Sets whether the given column is collapsible. Note that collapsible
  1132. * columns can only be actually collapsed (via UI or with
  1133. * {@link #setColumnCollapsed(Object, boolean) setColumnCollapsed()}) if
  1134. * {@link #isColumnCollapsingAllowed()} is true. By default all columns are
  1135. * collapsible.
  1136. *
  1137. * @param propertyId
  1138. * the propertyID identifying the column.
  1139. * @param collapsible
  1140. * true if the column should be collapsible, false otherwise.
  1141. */
  1142. public void setColumnCollapsible(Object propertyId, boolean collapsible) {
  1143. if (collapsible) {
  1144. noncollapsibleColumns.remove(propertyId);
  1145. } else {
  1146. noncollapsibleColumns.add(propertyId);
  1147. collapsedColumns.remove(propertyId);
  1148. }
  1149. refreshRowCache();
  1150. }
  1151. /**
  1152. * Checks if the given column is collapsible. Note that even if this method
  1153. * returns <code>true</code>, the column can only be actually collapsed (via
  1154. * UI or with {@link #setColumnCollapsed(Object, boolean)
  1155. * setColumnCollapsed()}) if {@link #isColumnCollapsingAllowed()} is also
  1156. * true.
  1157. *
  1158. * @return true if the column can be collapsed; false otherwise.
  1159. */
  1160. public boolean isColumnCollapsible(Object propertyId) {
  1161. return !noncollapsibleColumns.contains(propertyId);
  1162. }
  1163. /**
  1164. * Checks if column reordering is allowed.
  1165. *
  1166. * @return true if columns can be reordered; false otherwise.
  1167. */
  1168. public boolean isColumnReorderingAllowed() {
  1169. return columnReorderingAllowed;
  1170. }
  1171. /**
  1172. * Sets whether column reordering is allowed or not.
  1173. *
  1174. * @param columnReorderingAllowed
  1175. * specifies whether column reordering is allowed.
  1176. */
  1177. public void setColumnReorderingAllowed(boolean columnReorderingAllowed) {
  1178. if (columnReorderingAllowed != this.columnReorderingAllowed) {
  1179. this.columnReorderingAllowed = columnReorderingAllowed;
  1180. requestRepaint();
  1181. }
  1182. }
  1183. /*
  1184. * Arranges visible columns according to given columnOrder. Silently ignores
  1185. * colimnId:s that are not visible columns, and keeps the internal order of
  1186. * visible columns left out of the ordering (trailing). Silently does
  1187. * nothing if columnReordering is not allowed.
  1188. */
  1189. private void setColumnOrder(Object[] columnOrder) {
  1190. if (columnOrder == null || !isColumnReorderingAllowed()) {
  1191. return;
  1192. }
  1193. final LinkedList<Object> newOrder = new LinkedList<Object>();
  1194. for (int i = 0; i < columnOrder.length; i++) {
  1195. if (columnOrder[i] != null
  1196. && visibleColumns.contains(columnOrder[i])) {
  1197. visibleColumns.remove(columnOrder[i]);
  1198. newOrder.add(columnOrder[i]);
  1199. }
  1200. }
  1201. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1202. .hasNext();) {
  1203. final Object columnId = it.next();
  1204. if (!newOrder.contains(columnId)) {
  1205. newOrder.add(columnId);
  1206. }
  1207. }
  1208. visibleColumns = newOrder;
  1209. // Assure visual refresh
  1210. refreshRowCache();
  1211. }
  1212. /**
  1213. * Getter for property currentPageFirstItem.
  1214. *
  1215. * @return the Value of property currentPageFirstItem.
  1216. */
  1217. public int getCurrentPageFirstItemIndex() {
  1218. return currentPageFirstItemIndex;
  1219. }
  1220. void setCurrentPageFirstItemIndex(int newIndex, boolean needsPageBufferReset) {
  1221. if (newIndex < 0) {
  1222. newIndex = 0;
  1223. }
  1224. /*
  1225. * minimize Container.size() calls which may be expensive. For example
  1226. * it may cause sql query.
  1227. */
  1228. final int size = size();
  1229. /*
  1230. * The table is not capable of displaying an item in the container as
  1231. * the first if there are not enough items following the selected item
  1232. * so the whole table (pagelength) is filled.
  1233. */
  1234. int maxIndex = size - pageLength;
  1235. if (maxIndex < 0) {
  1236. maxIndex = 0;
  1237. }
  1238. /*
  1239. * FIXME #7607 Take somehow into account the case where we want to
  1240. * scroll to the bottom so that the last row is completely visible even
  1241. * if (table height) / (row height) is not an integer. Reverted the
  1242. * original fix because of #8662 regression.
  1243. */
  1244. if (newIndex > maxIndex) {
  1245. newIndex = maxIndex;
  1246. }
  1247. // Refresh first item id
  1248. if (items instanceof Container.Indexed) {
  1249. try {
  1250. currentPageFirstItemId = getIdByIndex(newIndex);
  1251. } catch (final IndexOutOfBoundsException e) {
  1252. currentPageFirstItemId = null;
  1253. }
  1254. currentPageFirstItemIndex = newIndex;
  1255. } else {
  1256. // For containers not supporting indexes, we must iterate the
  1257. // container forwards / backwards
  1258. // next available item forward or backward
  1259. currentPageFirstItemId = firstItemId();
  1260. // Go forwards in the middle of the list (respect borders)
  1261. while (currentPageFirstItemIndex < newIndex
  1262. && !isLastId(currentPageFirstItemId)) {
  1263. currentPageFirstItemIndex++;
  1264. currentPageFirstItemId = nextItemId(currentPageFirstItemId);
  1265. }
  1266. // If we did hit the border
  1267. if (isLastId(currentPageFirstItemId)) {
  1268. currentPageFirstItemIndex = size - 1;
  1269. }
  1270. // Go backwards in the middle of the list (respect borders)
  1271. while (currentPageFirstItemIndex > newIndex
  1272. && !isFirstId(currentPageFirstItemId)) {
  1273. currentPageFirstItemIndex--;
  1274. currentPageFirstItemId = prevItemId(currentPageFirstItemId);
  1275. }
  1276. // If we did hit the border
  1277. if (isFirstId(currentPageFirstItemId)) {
  1278. currentPageFirstItemIndex = 0;
  1279. }
  1280. // Go forwards once more
  1281. while (currentPageFirstItemIndex < newIndex
  1282. && !isLastId(currentPageFirstItemId)) {
  1283. currentPageFirstItemIndex++;
  1284. currentPageFirstItemId = nextItemId(currentPageFirstItemId);
  1285. }
  1286. // If for some reason we do hit border again, override
  1287. // the user index request
  1288. if (isLastId(currentPageFirstItemId)) {
  1289. newIndex = currentPageFirstItemIndex = size - 1;
  1290. }
  1291. }
  1292. if (needsPageBufferReset) {
  1293. // Assures the visual refresh
  1294. refreshRowCache();
  1295. }
  1296. }
  1297. /**
  1298. * Setter for property currentPageFirstItem.
  1299. *
  1300. * @param newIndex
  1301. * the New value of property currentPageFirstItem.
  1302. */
  1303. public void setCurrentPageFirstItemIndex(int newIndex) {
  1304. setCurrentPageFirstItemIndex(newIndex, true);
  1305. }
  1306. /**
  1307. * Getter for property pageBuffering.
  1308. *
  1309. * @deprecated functionality is not needed in ajax rendering model
  1310. *
  1311. * @return the Value of property pageBuffering.
  1312. */
  1313. @Deprecated
  1314. public boolean isPageBufferingEnabled() {
  1315. return true;
  1316. }
  1317. /**
  1318. * Setter for property pageBuffering.
  1319. *
  1320. * @deprecated functionality is not needed in ajax rendering model
  1321. *
  1322. * @param pageBuffering
  1323. * the New value of property pageBuffering.
  1324. */
  1325. @Deprecated
  1326. public void setPageBufferingEnabled(boolean pageBuffering) {
  1327. }
  1328. /**
  1329. * Getter for property selectable.
  1330. *
  1331. * <p>
  1332. * The table is not selectable by default.
  1333. * </p>
  1334. *
  1335. * @return the Value of property selectable.
  1336. */
  1337. public boolean isSelectable() {
  1338. return selectable;
  1339. }
  1340. /**
  1341. * Setter for property selectable.
  1342. *
  1343. * <p>
  1344. * The table is not selectable by default.
  1345. * </p>
  1346. *
  1347. * @param selectable
  1348. * the New value of property selectable.
  1349. */
  1350. public void setSelectable(boolean selectable) {
  1351. if (this.selectable != selectable) {
  1352. this.selectable = selectable;
  1353. requestRepaint();
  1354. }
  1355. }
  1356. /**
  1357. * Getter for property columnHeaderMode.
  1358. *
  1359. * @return the Value of property columnHeaderMode.
  1360. */
  1361. public ColumnHeaderMode getColumnHeaderMode() {
  1362. return columnHeaderMode;
  1363. }
  1364. /**
  1365. * Setter for property columnHeaderMode.
  1366. *
  1367. * @param columnHeaderMode
  1368. * the New value of property columnHeaderMode.
  1369. */
  1370. public void setColumnHeaderMode(ColumnHeaderMode columnHeaderMode) {
  1371. if (columnHeaderMode == null) {
  1372. throw new IllegalArgumentException(
  1373. "Column header mode can not be null");
  1374. }
  1375. if (columnHeaderMode != this.columnHeaderMode) {
  1376. this.columnHeaderMode = columnHeaderMode;
  1377. requestRepaint();
  1378. }
  1379. }
  1380. /**
  1381. * Refreshes the rows in the internal cache. Only if
  1382. * {@link #resetPageBuffer()} is called before this then all values are
  1383. * guaranteed to be recreated.
  1384. */
  1385. protected void refreshRenderedCells() {
  1386. if (getParent() == null) {
  1387. return;
  1388. }
  1389. if (!isContentRefreshesEnabled) {
  1390. return;
  1391. }
  1392. // Collects the basic facts about the table page
  1393. final int pagelen = getPageLength();
  1394. int firstIndex = getCurrentPageFirstItemIndex();
  1395. int rows, totalRows;
  1396. rows = totalRows = size();
  1397. if (rows > 0 && firstIndex >= 0) {
  1398. rows -= firstIndex;
  1399. }
  1400. if (pagelen > 0 && pagelen < rows) {
  1401. rows = pagelen;
  1402. }
  1403. // If "to be painted next" variables are set, use them
  1404. if (lastToBeRenderedInClient - firstToBeRenderedInClient > 0) {
  1405. rows = lastToBeRenderedInClient - firstToBeRenderedInClient + 1;
  1406. }
  1407. if (firstToBeRenderedInClient >= 0) {
  1408. if (firstToBeRenderedInClient < totalRows) {
  1409. firstIndex = firstToBeRenderedInClient;
  1410. } else {
  1411. firstIndex = totalRows - 1;
  1412. }
  1413. } else {
  1414. // initial load
  1415. // #8805 send one extra row in the beginning in case a partial
  1416. // row is shown on the UI
  1417. if (firstIndex > 0) {
  1418. firstIndex = firstIndex - 1;
  1419. rows = rows + 1;
  1420. }
  1421. firstToBeRenderedInClient = firstIndex;
  1422. }
  1423. if (totalRows > 0) {
  1424. if (rows + firstIndex > totalRows) {
  1425. rows = totalRows - firstIndex;
  1426. }
  1427. } else {
  1428. rows = 0;
  1429. }
  1430. // Saves the results to internal buffer
  1431. pageBuffer = getVisibleCellsNoCache(firstIndex, rows, true);
  1432. if (rows > 0) {
  1433. pageBufferFirstIndex = firstIndex;
  1434. }
  1435. setRowCacheInvalidated(true);
  1436. requestRepaint();
  1437. }
  1438. /**
  1439. * Requests that the Table should be repainted as soon as possible.
  1440. *
  1441. * Note that a {@code Table} does not necessarily repaint its contents when
  1442. * this method has been called. See {@link #refreshRowCache()} for forcing
  1443. * an update of the contents.
  1444. */
  1445. @Override
  1446. public void requestRepaint() {
  1447. // Overridden only for javadoc
  1448. super.requestRepaint();
  1449. }
  1450. @Override
  1451. public void requestRepaintAll() {
  1452. super.requestRepaintAll();
  1453. // Avoid sending a partial repaint (#8714)
  1454. refreshRowCache();
  1455. }
  1456. private void removeRowsFromCacheAndFillBottom(int firstIndex, int rows) {
  1457. int totalCachedRows = pageBuffer[CELL_ITEMID].length;
  1458. int totalRows = size();
  1459. int firstIndexInPageBuffer = firstIndex - pageBufferFirstIndex;
  1460. /*
  1461. * firstIndexInPageBuffer is the first row to be removed. "rows" rows
  1462. * after that should be removed. If the page buffer does not contain
  1463. * that many rows, we only remove the rows that actually are in the page
  1464. * buffer.
  1465. */
  1466. if (firstIndexInPageBuffer + rows > totalCachedRows) {
  1467. rows = totalCachedRows - firstIndexInPageBuffer;
  1468. }
  1469. /*
  1470. * Unregister components that will no longer be in the page buffer to
  1471. * make sure that no components leak.
  1472. */
  1473. unregisterComponentsAndPropertiesInRows(firstIndex, rows);
  1474. /*
  1475. * The number of rows that should be in the cache after this operation
  1476. * is done (pageBuffer currently contains the expanded items).
  1477. */
  1478. int newCachedRowCount = totalCachedRows;
  1479. if (newCachedRowCount + pageBufferFirstIndex > totalRows) {
  1480. newCachedRowCount = totalRows - pageBufferFirstIndex;
  1481. }
  1482. /*
  1483. * The index at which we should render the first row that does not come
  1484. * from the previous page buffer.
  1485. */
  1486. int firstAppendedRowInPageBuffer = totalCachedRows - rows;
  1487. int firstAppendedRow = firstAppendedRowInPageBuffer
  1488. + pageBufferFirstIndex;
  1489. /*
  1490. * Calculate the maximum number of new rows that we can add to the page
  1491. * buffer. Less than the rows we removed if the container does not
  1492. * contain that many items afterwards.
  1493. */
  1494. int maxRowsToRender = (totalRows - firstAppendedRow);
  1495. int rowsToAdd = rows;
  1496. if (rowsToAdd > maxRowsToRender) {
  1497. rowsToAdd = maxRowsToRender;
  1498. }
  1499. Object[][] cells = null;
  1500. if (rowsToAdd > 0) {
  1501. cells = getVisibleCellsNoCache(firstAppendedRow, rowsToAdd, false);
  1502. }
  1503. /*
  1504. * Create the new cache buffer by copying the first rows from the old
  1505. * buffer, moving the following rows upwards and appending more rows if
  1506. * applicable.
  1507. */
  1508. Object[][] newPageBuffer = new Object[pageBuffer.length][newCachedRowCount];
  1509. for (int i = 0; i < pageBuffer.length; i++) {
  1510. for (int row = 0; row < firstIndexInPageBuffer; row++) {
  1511. // Copy the first rows
  1512. newPageBuffer[i][row] = pageBuffer[i][row];
  1513. }
  1514. for (int row = firstIndexInPageBuffer; row < firstAppendedRowInPageBuffer; row++) {
  1515. // Move the rows that were after the expanded rows
  1516. newPageBuffer[i][row] = pageBuffer[i][row + rows];
  1517. }
  1518. for (int row = firstAppendedRowInPageBuffer; row < newCachedRowCount; row++) {
  1519. // Add the newly rendered rows. Only used if rowsToAdd > 0
  1520. // (cells != null)
  1521. newPageBuffer[i][row] = cells[i][row
  1522. - firstAppendedRowInPageBuffer];
  1523. }
  1524. }
  1525. pageBuffer = newPageBuffer;
  1526. }
  1527. private Object[][] getVisibleCellsUpdateCacheRows(int firstIndex, int rows) {
  1528. Object[][] cells = getVisibleCellsNoCache(firstIndex, rows, false);
  1529. int cacheIx = firstIndex - pageBufferFirstIndex;
  1530. // update the new rows in the cache.
  1531. int totalCachedRows = pageBuffer[CELL_ITEMID].length;
  1532. int end = Math.min(cacheIx + rows, totalCachedRows);
  1533. for (int ix = cacheIx; ix < end; ix++) {
  1534. for (int i = 0; i < pageBuffer.length; i++) {
  1535. pageBuffer[i][ix] = cells[i][ix - cacheIx];
  1536. }
  1537. }
  1538. return cells;
  1539. }
  1540. /**
  1541. * @param firstIndex
  1542. * The position where new rows should be inserted
  1543. * @param rows
  1544. * The maximum number of rows that should be inserted at position
  1545. * firstIndex. Less rows will be inserted if the page buffer is
  1546. * too small.
  1547. * @return
  1548. */
  1549. private Object[][] getVisibleCellsInsertIntoCache(int firstIndex, int rows) {
  1550. getLogger().finest(
  1551. "Insert " + rows + " rows at index " + firstIndex
  1552. + " to existing page buffer requested");
  1553. // Page buffer must not become larger than pageLength*cacheRate before
  1554. // or after the current page
  1555. int minPageBufferIndex = getCurrentPageFirstItemIndex()
  1556. - (int) (getPageLength() * getCacheRate());
  1557. if (minPageBufferIndex < 0) {
  1558. minPageBufferIndex = 0;
  1559. }
  1560. int maxPageBufferIndex = getCurrentPageFirstItemIndex()
  1561. + (int) (getPageLength() * (1 + getCacheRate()));
  1562. int maxBufferSize = maxPageBufferIndex - minPageBufferIndex;
  1563. if (getPageLength() == 0) {
  1564. // If pageLength == 0 then all rows should be rendered
  1565. maxBufferSize = pageBuffer[0].length + rows;
  1566. }
  1567. /*
  1568. * Number of rows that were previously cached. This is not necessarily
  1569. * the same as pageLength if we do not have enough rows in the
  1570. * container.
  1571. */
  1572. int currentlyCachedRowCount = pageBuffer[CELL_ITEMID].length;
  1573. /*
  1574. * firstIndexInPageBuffer is the offset in pageBuffer where the new rows
  1575. * will be inserted (firstIndex is the index in the whole table).
  1576. *
  1577. * E.g. scrolled down to row 1000: firstIndex==1010,
  1578. * pageBufferFirstIndex==1000 -> cacheIx==10
  1579. */
  1580. int firstIndexInPageBuffer = firstIndex - pageBufferFirstIndex;
  1581. /* If rows > size available in page buffer */
  1582. if (firstIndexInPageBuffer + rows > maxBufferSize) {
  1583. rows = maxBufferSize - firstIndexInPageBuffer;
  1584. }
  1585. /*
  1586. * "rows" rows will be inserted at firstIndex. Find out how many old
  1587. * rows fall outside the new buffer so we can unregister components in
  1588. * the cache.
  1589. */
  1590. /* All rows until the insertion point remain, always. */
  1591. int firstCacheRowToRemoveInPageBuffer = firstIndexInPageBuffer;
  1592. /*
  1593. * IF there is space remaining in the buffer after the rows have been
  1594. * inserted, we can keep more rows.
  1595. */
  1596. int numberOfOldRowsAfterInsertedRows = maxBufferSize
  1597. - firstIndexInPageBuffer - rows;
  1598. if (numberOfOldRowsAfterInsertedRows > 0) {
  1599. firstCacheRowToRemoveInPageBuffer += numberOfOldRowsAfterInsertedRows;
  1600. }
  1601. if (firstCacheRowToRemoveInPageBuffer <= currentlyCachedRowCount) {
  1602. /*
  1603. * Unregister all components that fall beyond the cache limits after
  1604. * inserting the new rows.
  1605. */
  1606. unregisterComponentsAndPropertiesInRows(
  1607. firstCacheRowToRemoveInPageBuffer + pageBufferFirstIndex,
  1608. currentlyCachedRowCount - firstCacheRowToRemoveInPageBuffer
  1609. + pageBufferFirstIndex);
  1610. }
  1611. // Calculate the new cache size
  1612. int newCachedRowCount = currentlyCachedRowCount;
  1613. if (maxBufferSize == 0 || currentlyCachedRowCount < maxBufferSize) {
  1614. newCachedRowCount = currentlyCachedRowCount + rows;
  1615. if (maxBufferSize > 0 && newCachedRowCount > maxBufferSize) {
  1616. newCachedRowCount = maxBufferSize;
  1617. }
  1618. }
  1619. /* Paint the new rows into a separate buffer */
  1620. Object[][] cells = getVisibleCellsNoCache(firstIndex, rows, false);
  1621. /*
  1622. * Create the new cache buffer and fill it with the data from the old
  1623. * buffer as well as the inserted rows.
  1624. */
  1625. Object[][] newPageBuffer = new Object[pageBuffer.length][newCachedRowCount];
  1626. for (int i = 0; i < pageBuffer.length; i++) {
  1627. for (int row = 0; row < firstIndexInPageBuffer; row++) {
  1628. // Copy the first rows
  1629. newPageBuffer[i][row] = pageBuffer[i][row];
  1630. }
  1631. for (int row = firstIndexInPageBuffer; row < firstIndexInPageBuffer
  1632. + rows; row++) {
  1633. // Copy the newly created rows
  1634. newPageBuffer[i][row] = cells[i][row - firstIndexInPageBuffer];
  1635. }
  1636. for (int row = firstIndexInPageBuffer + rows; row < newCachedRowCount; row++) {
  1637. // Move the old rows down below the newly inserted rows
  1638. newPageBuffer[i][row] = pageBuffer[i][row - rows];
  1639. }
  1640. }
  1641. pageBuffer = newPageBuffer;
  1642. getLogger().finest(
  1643. "Page Buffer now contains "
  1644. + pageBuffer[CELL_ITEMID].length
  1645. + " rows ("
  1646. + pageBufferFirstIndex
  1647. + "-"
  1648. + (pageBufferFirstIndex
  1649. + pageBuffer[CELL_ITEMID].length - 1) + ")");
  1650. return cells;
  1651. }
  1652. /**
  1653. * Render rows with index "firstIndex" to "firstIndex+rows-1" to a new
  1654. * buffer.
  1655. *
  1656. * Reuses values from the current page buffer if the rows are found there.
  1657. *
  1658. * @param firstIndex
  1659. * @param rows
  1660. * @param replaceListeners
  1661. * @return
  1662. */
  1663. private Object[][] getVisibleCellsNoCache(int firstIndex, int rows,
  1664. boolean replaceListeners) {
  1665. getLogger().finest(
  1666. "Render visible cells for rows " + firstIndex + "-"
  1667. + (firstIndex + rows - 1));
  1668. final Object[] colids = getVisibleColumns();
  1669. final int cols = colids.length;
  1670. HashSet<Property<?>> oldListenedProperties = listenedProperties;
  1671. HashSet<Component> oldVisibleComponents = visibleComponents;
  1672. if (replaceListeners) {
  1673. // initialize the listener collections, this should only be done if
  1674. // the entire cache is refreshed (through refreshRenderedCells)
  1675. listenedProperties = new HashSet<Property<?>>();
  1676. visibleComponents = new HashSet<Component>();
  1677. }
  1678. Object[][] cells = new Object[cols + CELL_FIRSTCOL][rows];
  1679. if (rows == 0) {
  1680. unregisterPropertiesAndComponents(oldListenedProperties,
  1681. oldVisibleComponents);
  1682. return cells;
  1683. }
  1684. // Gets the first item id
  1685. Object id;
  1686. if (items instanceof Container.Indexed) {
  1687. id = getIdByIndex(firstIndex);
  1688. } else {
  1689. id = firstItemId();
  1690. for (int i = 0; i < firstIndex; i++) {
  1691. id = nextItemId(id);
  1692. }
  1693. }
  1694. final RowHeaderMode headmode = getRowHeaderMode();
  1695. final boolean[] iscomponent = new boolean[cols];
  1696. for (int i = 0; i < cols; i++) {
  1697. iscomponent[i] = columnGenerators.containsKey(colids[i])
  1698. || Component.class.isAssignableFrom(getType(colids[i]));
  1699. }
  1700. int firstIndexNotInCache;
  1701. if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) {
  1702. firstIndexNotInCache = pageBufferFirstIndex
  1703. + pageBuffer[CELL_ITEMID].length;
  1704. } else {
  1705. firstIndexNotInCache = -1;
  1706. }
  1707. // Creates the page contents
  1708. int filledRows = 0;
  1709. for (int i = 0; i < rows && id != null; i++) {
  1710. cells[CELL_ITEMID][i] = id;
  1711. cells[CELL_KEY][i] = itemIdMapper.key(id);
  1712. if (headmode != ROW_HEADER_MODE_HIDDEN) {
  1713. switch (headmode) {
  1714. case INDEX:
  1715. cells[CELL_HEADER][i] = String.valueOf(i + firstIndex + 1);
  1716. break;
  1717. default:
  1718. cells[CELL_HEADER][i] = getItemCaption(id);
  1719. }
  1720. cells[CELL_ICON][i] = getItemIcon(id);
  1721. }
  1722. GeneratedRow generatedRow = rowGenerator != null ? rowGenerator
  1723. .generateRow(this, id) : null;
  1724. cells[CELL_GENERATED_ROW][i] = generatedRow;
  1725. for (int j = 0; j < cols; j++) {
  1726. if (isColumnCollapsed(colids[j])) {
  1727. continue;
  1728. }
  1729. Property<?> p = null;
  1730. Object value = "";
  1731. boolean isGeneratedRow = generatedRow != null;
  1732. boolean isGeneratedColumn = columnGenerators
  1733. .containsKey(colids[j]);
  1734. boolean isGenerated = isGeneratedRow || isGeneratedColumn;
  1735. if (!isGenerated) {
  1736. p = getContainerProperty(id, colids[j]);
  1737. }
  1738. if (isGeneratedRow) {
  1739. if (generatedRow.isSpanColumns() && j > 0) {
  1740. value = null;
  1741. } else if (generatedRow.isSpanColumns() && j == 0
  1742. && generatedRow.getValue() instanceof Component) {
  1743. value = generatedRow.getValue();
  1744. } else if (generatedRow.getText().length > j) {
  1745. value = generatedRow.getText()[j];
  1746. }
  1747. } else {
  1748. // check in current pageBuffer already has row
  1749. int index = firstIndex + i;
  1750. if (p != null || isGenerated) {
  1751. int indexInOldBuffer = index - pageBufferFirstIndex;
  1752. if (index < firstIndexNotInCache
  1753. && index >= pageBufferFirstIndex
  1754. && pageBuffer[CELL_GENERATED_ROW][indexInOldBuffer] == null
  1755. && id.equals(pageBuffer[CELL_ITEMID][indexInOldBuffer])) {
  1756. // we already have data in our cache,
  1757. // recycle it instead of fetching it via
  1758. // getValue/getPropertyValue
  1759. value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer];
  1760. if (!isGeneratedColumn && iscomponent[j]
  1761. || !(value instanceof Component)) {
  1762. listenProperty(p, oldListenedProperties);
  1763. }
  1764. } else {
  1765. if (isGeneratedColumn) {
  1766. ColumnGenerator cg = columnGenerators
  1767. .get(colids[j]);
  1768. value = cg.generateCell(this, id, colids[j]);
  1769. if (value != null
  1770. && !(value instanceof Component)
  1771. && !(value instanceof String)) {
  1772. // Avoid errors if a generator returns
  1773. // something
  1774. // other than a Component or a String
  1775. value = value.toString();
  1776. }
  1777. } else if (iscomponent[j]) {
  1778. value = p.getValue();
  1779. listenProperty(p, oldListenedProperties);
  1780. } else if (p != null) {
  1781. value = getPropertyValue(id, colids[j], p);
  1782. /*
  1783. * If returned value is Component (via
  1784. * fieldfactory or overridden getPropertyValue)
  1785. * we excpect it to listen property value
  1786. * changes. Otherwise if property emits value
  1787. * change events, table will start to listen
  1788. * them and refresh content when needed.
  1789. */
  1790. if (!(value instanceof Component)) {
  1791. listenProperty(p, oldListenedProperties);
  1792. }
  1793. } else {
  1794. value = getPropertyValue(id, colids[j], null);
  1795. }
  1796. }
  1797. }
  1798. }
  1799. if (value instanceof Component) {
  1800. registerComponent((Component) value);
  1801. }
  1802. cells[CELL_FIRSTCOL + j][i] = value;
  1803. }
  1804. // Gets the next item id
  1805. if (items instanceof Container.Indexed) {
  1806. int index = firstIndex + i + 1;
  1807. if (index < size()) {
  1808. id = getIdByIndex(index);
  1809. } else {
  1810. id = null;
  1811. }
  1812. } else {
  1813. id = nextItemId(id);
  1814. }
  1815. filledRows++;
  1816. }
  1817. // Assures that all the rows of the cell-buffer are valid
  1818. if (filledRows != cells[0].length) {
  1819. final Object[][] temp = new Object[cells.length][filledRows];
  1820. for (int i = 0; i < cells.length; i++) {
  1821. for (int j = 0; j < filledRows; j++) {
  1822. temp[i][j] = cells[i][j];
  1823. }
  1824. }
  1825. cells = temp;
  1826. }
  1827. unregisterPropertiesAndComponents(oldListenedProperties,
  1828. oldVisibleComponents);
  1829. return cells;
  1830. }
  1831. protected void registerComponent(Component component) {
  1832. getLogger().finest(
  1833. "Registered " + component.getClass().getSimpleName() + ": "
  1834. + component.getCaption());
  1835. if (component.getParent() != this) {
  1836. component.setParent(this);
  1837. }
  1838. visibleComponents.add(component);
  1839. }
  1840. private void listenProperty(Property<?> p,
  1841. HashSet<Property<?>> oldListenedProperties) {
  1842. if (p instanceof Property.ValueChangeNotifier) {
  1843. if (oldListenedProperties == null
  1844. || !oldListenedProperties.contains(p)) {
  1845. ((Property.ValueChangeNotifier) p).addListener(this);
  1846. }
  1847. /*
  1848. * register listened properties, so we can do proper cleanup to free
  1849. * memory. Essential if table has loads of data and it is used for a
  1850. * long time.
  1851. */
  1852. listenedProperties.add(p);
  1853. }
  1854. }
  1855. /**
  1856. * @param firstIx
  1857. * Index of the first row to process. Global index, not relative
  1858. * to page buffer.
  1859. * @param count
  1860. */
  1861. private void unregisterComponentsAndPropertiesInRows(int firstIx, int count) {
  1862. getLogger().finest(
  1863. "Unregistering components in rows " + firstIx + "-"
  1864. + (firstIx + count - 1));
  1865. Object[] colids = getVisibleColumns();
  1866. if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) {
  1867. int bufSize = pageBuffer[CELL_ITEMID].length;
  1868. int ix = firstIx - pageBufferFirstIndex;
  1869. ix = ix < 0 ? 0 : ix;
  1870. if (ix < bufSize) {
  1871. count = count > bufSize - ix ? bufSize - ix : count;
  1872. for (int i = 0; i < count; i++) {
  1873. for (int c = 0; c < colids.length; c++) {
  1874. Object cellVal = pageBuffer[CELL_FIRSTCOL + c][i + ix];
  1875. if (cellVal instanceof Component
  1876. && visibleComponents.contains(cellVal)) {
  1877. visibleComponents.remove(cellVal);
  1878. unregisterComponent((Component) cellVal);
  1879. } else {
  1880. Property<?> p = getContainerProperty(
  1881. pageBuffer[CELL_ITEMID][i + ix], colids[c]);
  1882. if (p instanceof ValueChangeNotifier
  1883. && listenedProperties.contains(p)) {
  1884. listenedProperties.remove(p);
  1885. ((ValueChangeNotifier) p).removeListener(this);
  1886. }
  1887. }
  1888. }
  1889. }
  1890. }
  1891. }
  1892. }
  1893. /**
  1894. * Helper method to remove listeners and maintain correct component
  1895. * hierarchy. Detaches properties and components if those are no more
  1896. * rendered in client.
  1897. *
  1898. * @param oldListenedProperties
  1899. * set of properties that where listened in last render
  1900. * @param oldVisibleComponents
  1901. * set of components that where attached in last render
  1902. */
  1903. private void unregisterPropertiesAndComponents(
  1904. HashSet<Property<?>> oldListenedProperties,
  1905. HashSet<Component> oldVisibleComponents) {
  1906. if (oldVisibleComponents != null) {
  1907. for (final Iterator<Component> i = oldVisibleComponents.iterator(); i
  1908. .hasNext();) {
  1909. Component c = i.next();
  1910. if (!visibleComponents.contains(c)) {
  1911. unregisterComponent(c);
  1912. }
  1913. }
  1914. }
  1915. if (oldListenedProperties != null) {
  1916. for (final Iterator<Property<?>> i = oldListenedProperties
  1917. .iterator(); i.hasNext();) {
  1918. Property.ValueChangeNotifier o = (ValueChangeNotifier) i.next();
  1919. if (!listenedProperties.contains(o)) {
  1920. o.removeListener(this);
  1921. }
  1922. }
  1923. }
  1924. }
  1925. /**
  1926. * This method cleans up a Component that has been generated when Table is
  1927. * in editable mode. The component needs to be detached from its parent and
  1928. * if it is a field, it needs to be detached from its property data source
  1929. * in order to allow garbage collection to take care of removing the unused
  1930. * component from memory.
  1931. *
  1932. * Override this method and getPropertyValue(Object, Object, Property) with
  1933. * custom logic if you need to deal with buffered fields.
  1934. *
  1935. * @see #getPropertyValue(Object, Object, Property)
  1936. *
  1937. * @param oldVisibleComponents
  1938. * a set of components that should be unregistered.
  1939. */
  1940. protected void unregisterComponent(Component component) {
  1941. getLogger().finest(
  1942. "Unregistered " + component.getClass().getSimpleName() + ": "
  1943. + component.getCaption());
  1944. component.setParent(null);
  1945. /*
  1946. * Also remove property data sources to unregister listeners keeping the
  1947. * fields in memory.
  1948. */
  1949. if (component instanceof Field) {
  1950. Field<?> field = (Field<?>) component;
  1951. Property<?> associatedProperty = associatedProperties
  1952. .remove(component);
  1953. if (associatedProperty != null
  1954. && field.getPropertyDataSource() == associatedProperty) {
  1955. // Remove the property data source only if it's the one we
  1956. // added in getPropertyValue
  1957. field.setPropertyDataSource(null);
  1958. }
  1959. }
  1960. }
  1961. /**
  1962. * Refreshes the current page contents.
  1963. *
  1964. * @deprecated should not need to be used
  1965. */
  1966. @Deprecated
  1967. public void refreshCurrentPage() {
  1968. }
  1969. /**
  1970. * Sets the row header mode.
  1971. * <p>
  1972. * The mode can be one of the following ones:
  1973. * <ul>
  1974. * <li>{@link #ROW_HEADER_MODE_HIDDEN}: The row captions are hidden.</li>
  1975. * <li>{@link #ROW_HEADER_MODE_ID}: Items Id-objects <code>toString()</code>
  1976. * is used as row caption.
  1977. * <li>{@link #ROW_HEADER_MODE_ITEM}: Item-objects <code>toString()</code>
  1978. * is used as row caption.
  1979. * <li>{@link #ROW_HEADER_MODE_PROPERTY}: Property set with
  1980. * {@link #setItemCaptionPropertyId(Object)} is used as row header.
  1981. * <li>{@link #ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID}: Items Id-objects
  1982. * <code>toString()</code> is used as row header. If caption is explicitly
  1983. * specified, it overrides the id-caption.
  1984. * <li>{@link #ROW_HEADER_MODE_EXPLICIT}: The row headers must be explicitly
  1985. * specified.</li>
  1986. * <li>{@link #ROW_HEADER_MODE_INDEX}: The index of the item is used as row
  1987. * caption. The index mode can only be used with the containers implementing
  1988. * <code>Container.Indexed</code> interface.</li>
  1989. * </ul>
  1990. * The default value is {@link #ROW_HEADER_MODE_HIDDEN}
  1991. * </p>
  1992. *
  1993. * @param mode
  1994. * the One of the modes listed above.
  1995. */
  1996. public void setRowHeaderMode(RowHeaderMode mode) {
  1997. if (mode != null) {
  1998. rowHeaderMode = mode;
  1999. if (mode != RowHeaderMode.HIDDEN) {
  2000. setItemCaptionMode(mode.getItemCaptionMode());
  2001. }
  2002. // Assures the visual refresh. No need to reset the page buffer
  2003. // before
  2004. // as the content has not changed, only the alignments.
  2005. refreshRenderedCells();
  2006. }
  2007. }
  2008. /**
  2009. * Gets the row header mode.
  2010. *
  2011. * @return the Row header mode.
  2012. * @see #setRowHeaderMode(int)
  2013. */
  2014. public RowHeaderMode getRowHeaderMode() {
  2015. return rowHeaderMode;
  2016. }
  2017. /**
  2018. * Adds the new row to table and fill the visible cells (except generated
  2019. * columns) with given values.
  2020. *
  2021. * @param cells
  2022. * the Object array that is used for filling the visible cells
  2023. * new row. The types must be settable to visible column property
  2024. * types.
  2025. * @param itemId
  2026. * the Id the new row. If null, a new id is automatically
  2027. * assigned. If given, the table cant already have a item with
  2028. * given id.
  2029. * @return Returns item id for the new row. Returns null if operation fails.
  2030. */
  2031. public Object addItem(Object[] cells, Object itemId)
  2032. throws UnsupportedOperationException {
  2033. // remove generated columns from the list of columns being assigned
  2034. final LinkedList<Object> availableCols = new LinkedList<Object>();
  2035. for (Iterator<Object> it = visibleColumns.iterator(); it.hasNext();) {
  2036. Object id = it.next();
  2037. if (!columnGenerators.containsKey(id)) {
  2038. availableCols.add(id);
  2039. }
  2040. }
  2041. // Checks that a correct number of cells are given
  2042. if (cells.length != availableCols.size()) {
  2043. return null;
  2044. }
  2045. // Creates new item
  2046. Item item;
  2047. if (itemId == null) {
  2048. itemId = items.addItem();
  2049. if (itemId == null) {
  2050. return null;
  2051. }
  2052. item = items.getItem(itemId);
  2053. } else {
  2054. item = items.addItem(itemId);
  2055. }
  2056. if (item == null) {
  2057. return null;
  2058. }
  2059. // Fills the item properties
  2060. for (int i = 0; i < availableCols.size(); i++) {
  2061. item.getItemProperty(availableCols.get(i)).setValue(cells[i]);
  2062. }
  2063. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2064. refreshRowCache();
  2065. }
  2066. return itemId;
  2067. }
  2068. /**
  2069. * Discards and recreates the internal row cache. Call this if you make
  2070. * changes that affect the rows but the information about the changes are
  2071. * not automatically propagated to the Table.
  2072. * <p>
  2073. * Do not call this e.g. if you have updated the data model through a
  2074. * Property. These types of changes are automatically propagated to the
  2075. * Table.
  2076. * <p>
  2077. * A typical case when this is needed is if you update a generator (e.g.
  2078. * CellStyleGenerator) and want to ensure that the rows are redrawn with new
  2079. * styles.
  2080. * <p>
  2081. * <i>Note that calling this method is not cheap so avoid calling it
  2082. * unnecessarily.</i>
  2083. *
  2084. * @since 6.7.2
  2085. */
  2086. public void refreshRowCache() {
  2087. resetPageBuffer();
  2088. refreshRenderedCells();
  2089. }
  2090. @Override
  2091. public void setContainerDataSource(Container newDataSource) {
  2092. disableContentRefreshing();
  2093. if (newDataSource == null) {
  2094. newDataSource = new IndexedContainer();
  2095. }
  2096. // Assures that the data source is ordered by making unordered
  2097. // containers ordered by wrapping them
  2098. if (newDataSource instanceof Container.Ordered) {
  2099. super.setContainerDataSource(newDataSource);
  2100. } else {
  2101. super.setContainerDataSource(new ContainerOrderedWrapper(
  2102. newDataSource));
  2103. }
  2104. // Resets page position
  2105. currentPageFirstItemId = null;
  2106. currentPageFirstItemIndex = 0;
  2107. // Resets column properties
  2108. if (collapsedColumns != null) {
  2109. collapsedColumns.clear();
  2110. }
  2111. // columnGenerators 'override' properties, don't add the same id twice
  2112. Collection<Object> col = new LinkedList<Object>();
  2113. for (Iterator<?> it = getContainerPropertyIds().iterator(); it
  2114. .hasNext();) {
  2115. Object id = it.next();
  2116. if (columnGenerators == null || !columnGenerators.containsKey(id)) {
  2117. col.add(id);
  2118. }
  2119. }
  2120. // generators added last
  2121. if (columnGenerators != null && columnGenerators.size() > 0) {
  2122. col.addAll(columnGenerators.keySet());
  2123. }
  2124. setVisibleColumns(col.toArray());
  2125. // Assure visual refresh
  2126. resetPageBuffer();
  2127. enableContentRefreshing(true);
  2128. }
  2129. /**
  2130. * Gets items ids from a range of key values
  2131. *
  2132. * @param startRowKey
  2133. * The start key
  2134. * @param endRowKey
  2135. * The end key
  2136. * @return
  2137. */
  2138. private Set<Object> getItemIdsInRange(Object itemId, final int length) {
  2139. HashSet<Object> ids = new HashSet<Object>();
  2140. for (int i = 0; i < length; i++) {
  2141. assert itemId != null; // should not be null unless client-server
  2142. // are out of sync
  2143. ids.add(itemId);
  2144. itemId = nextItemId(itemId);
  2145. }
  2146. return ids;
  2147. }
  2148. /**
  2149. * Handles selection if selection is a multiselection
  2150. *
  2151. * @param variables
  2152. * The variables
  2153. */
  2154. private void handleSelectedItems(Map<String, Object> variables) {
  2155. final String[] ka = (String[]) variables.get("selected");
  2156. final String[] ranges = (String[]) variables.get("selectedRanges");
  2157. Set<Object> renderedItemIds = getCurrentlyRenderedItemIds();
  2158. @SuppressWarnings("unchecked")
  2159. HashSet<Object> newValue = new HashSet<Object>(
  2160. (Collection<Object>) getValue());
  2161. if (variables.containsKey("clearSelections")) {
  2162. // the client side has instructed to swipe all previous selections
  2163. newValue.clear();
  2164. } else {
  2165. /*
  2166. * first clear all selections that are currently rendered rows (the
  2167. * ones that the client side counterpart is aware of)
  2168. */
  2169. newValue.removeAll(renderedItemIds);
  2170. }
  2171. /*
  2172. * Then add (possibly some of them back) rows that are currently
  2173. * selected on the client side (the ones that the client side is aware
  2174. * of).
  2175. */
  2176. for (int i = 0; i < ka.length; i++) {
  2177. // key to id
  2178. final Object id = itemIdMapper.get(ka[i]);
  2179. if (!isNullSelectionAllowed()
  2180. && (id == null || id == getNullSelectionItemId())) {
  2181. // skip empty selection if nullselection is not allowed
  2182. requestRepaint();
  2183. } else if (id != null && containsId(id)) {
  2184. newValue.add(id);
  2185. }
  2186. }
  2187. /* Add range items aka shift clicked multiselection areas */
  2188. if (ranges != null) {
  2189. for (String range : ranges) {
  2190. String[] split = range.split("-");
  2191. Object startItemId = itemIdMapper.get(split[0]);
  2192. int length = Integer.valueOf(split[1]);
  2193. newValue.addAll(getItemIdsInRange(startItemId, length));
  2194. }
  2195. }
  2196. if (!isNullSelectionAllowed() && newValue.isEmpty()) {
  2197. // empty selection not allowed, keep old value
  2198. requestRepaint();
  2199. return;
  2200. }
  2201. setValue(newValue, true);
  2202. }
  2203. private Set<Object> getCurrentlyRenderedItemIds() {
  2204. HashSet<Object> ids = new HashSet<Object>();
  2205. if (pageBuffer != null) {
  2206. for (int i = 0; i < pageBuffer[CELL_ITEMID].length; i++) {
  2207. ids.add(pageBuffer[CELL_ITEMID][i]);
  2208. }
  2209. }
  2210. return ids;
  2211. }
  2212. /* Component basics */
  2213. /**
  2214. * Invoked when the value of a variable has changed.
  2215. *
  2216. * @see com.vaadin.ui.Select#changeVariables(java.lang.Object,
  2217. * java.util.Map)
  2218. */
  2219. @Override
  2220. public void changeVariables(Object source, Map<String, Object> variables) {
  2221. boolean clientNeedsContentRefresh = false;
  2222. handleClickEvent(variables);
  2223. handleColumnResizeEvent(variables);
  2224. handleColumnWidthUpdates(variables);
  2225. disableContentRefreshing();
  2226. if (!isSelectable() && variables.containsKey("selected")) {
  2227. // Not-selectable is a special case, AbstractSelect does not support
  2228. // TODO could be optimized.
  2229. variables = new HashMap<String, Object>(variables);
  2230. variables.remove("selected");
  2231. }
  2232. /*
  2233. * The AbstractSelect cannot handle the multiselection properly, instead
  2234. * we handle it ourself
  2235. */
  2236. else if (isSelectable() && isMultiSelect()
  2237. && variables.containsKey("selected")
  2238. && multiSelectMode == MultiSelectMode.DEFAULT) {
  2239. handleSelectedItems(variables);
  2240. variables = new HashMap<String, Object>(variables);
  2241. variables.remove("selected");
  2242. }
  2243. super.changeVariables(source, variables);
  2244. // Client might update the pagelength if Table height is fixed
  2245. if (variables.containsKey("pagelength")) {
  2246. // Sets pageLength directly to avoid repaint that setter causes
  2247. pageLength = (Integer) variables.get("pagelength");
  2248. }
  2249. // Page start index
  2250. if (variables.containsKey("firstvisible")) {
  2251. final Integer value = (Integer) variables.get("firstvisible");
  2252. if (value != null) {
  2253. setCurrentPageFirstItemIndex(value.intValue(), false);
  2254. }
  2255. }
  2256. // Sets requested firstrow and rows for the next paint
  2257. if (variables.containsKey("reqfirstrow")
  2258. || variables.containsKey("reqrows")) {
  2259. try {
  2260. firstToBeRenderedInClient = ((Integer) variables
  2261. .get("firstToBeRendered")).intValue();
  2262. lastToBeRenderedInClient = ((Integer) variables
  2263. .get("lastToBeRendered")).intValue();
  2264. } catch (Exception e) {
  2265. // FIXME: Handle exception
  2266. getLogger().log(Level.FINER,
  2267. "Could not parse the first and/or last rows.", e);
  2268. }
  2269. // respect suggested rows only if table is not otherwise updated
  2270. // (row caches emptied by other event)
  2271. if (!containerChangeToBeRendered) {
  2272. Integer value = (Integer) variables.get("reqfirstrow");
  2273. if (value != null) {
  2274. reqFirstRowToPaint = value.intValue();
  2275. }
  2276. value = (Integer) variables.get("reqrows");
  2277. if (value != null) {
  2278. reqRowsToPaint = value.intValue();
  2279. // sanity check
  2280. if (reqFirstRowToPaint + reqRowsToPaint > size()) {
  2281. reqRowsToPaint = size() - reqFirstRowToPaint;
  2282. }
  2283. }
  2284. }
  2285. getLogger().finest(
  2286. "Client wants rows " + reqFirstRowToPaint + "-"
  2287. + (reqFirstRowToPaint + reqRowsToPaint - 1));
  2288. clientNeedsContentRefresh = true;
  2289. }
  2290. if (!sortDisabled) {
  2291. // Sorting
  2292. boolean doSort = false;
  2293. if (variables.containsKey("sortcolumn")) {
  2294. final String colId = (String) variables.get("sortcolumn");
  2295. if (colId != null && !"".equals(colId) && !"null".equals(colId)) {
  2296. final Object id = columnIdMap.get(colId);
  2297. setSortContainerPropertyId(id, false);
  2298. doSort = true;
  2299. }
  2300. }
  2301. if (variables.containsKey("sortascending")) {
  2302. final boolean state = ((Boolean) variables.get("sortascending"))
  2303. .booleanValue();
  2304. if (state != sortAscending) {
  2305. setSortAscending(state, false);
  2306. doSort = true;
  2307. }
  2308. }
  2309. if (doSort) {
  2310. this.sort();
  2311. resetPageBuffer();
  2312. }
  2313. }
  2314. // Dynamic column hide/show and order
  2315. // Update visible columns
  2316. if (isColumnCollapsingAllowed()) {
  2317. if (variables.containsKey("collapsedcolumns")) {
  2318. try {
  2319. final Object[] ids = (Object[]) variables
  2320. .get("collapsedcolumns");
  2321. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2322. .hasNext();) {
  2323. setColumnCollapsed(it.next(), false);
  2324. }
  2325. for (int i = 0; i < ids.length; i++) {
  2326. setColumnCollapsed(columnIdMap.get(ids[i].toString()),
  2327. true);
  2328. }
  2329. } catch (final Exception e) {
  2330. // FIXME: Handle exception
  2331. getLogger().log(Level.FINER,
  2332. "Could not determine column collapsing state", e);
  2333. }
  2334. clientNeedsContentRefresh = true;
  2335. }
  2336. }
  2337. if (isColumnReorderingAllowed()) {
  2338. if (variables.containsKey("columnorder")) {
  2339. try {
  2340. final Object[] ids = (Object[]) variables
  2341. .get("columnorder");
  2342. // need a real Object[], ids can be a String[]
  2343. final Object[] idsTemp = new Object[ids.length];
  2344. for (int i = 0; i < ids.length; i++) {
  2345. idsTemp[i] = columnIdMap.get(ids[i].toString());
  2346. }
  2347. setColumnOrder(idsTemp);
  2348. if (hasListeners(ColumnReorderEvent.class)) {
  2349. fireEvent(new ColumnReorderEvent(this));
  2350. }
  2351. } catch (final Exception e) {
  2352. // FIXME: Handle exception
  2353. getLogger().log(Level.FINER,
  2354. "Could not determine column reordering state", e);
  2355. }
  2356. clientNeedsContentRefresh = true;
  2357. }
  2358. }
  2359. enableContentRefreshing(clientNeedsContentRefresh);
  2360. // Actions
  2361. if (variables.containsKey("action")) {
  2362. final StringTokenizer st = new StringTokenizer(
  2363. (String) variables.get("action"), ",");
  2364. if (st.countTokens() == 2) {
  2365. final Object itemId = itemIdMapper.get(st.nextToken());
  2366. final Action action = actionMapper.get(st.nextToken());
  2367. if (action != null && (itemId == null || containsId(itemId))
  2368. && actionHandlers != null) {
  2369. for (Handler ah : actionHandlers) {
  2370. ah.handleAction(action, this, itemId);
  2371. }
  2372. }
  2373. }
  2374. }
  2375. }
  2376. /**
  2377. * Handles click event
  2378. *
  2379. * @param variables
  2380. */
  2381. private void handleClickEvent(Map<String, Object> variables) {
  2382. // Item click event
  2383. if (variables.containsKey("clickEvent")) {
  2384. String key = (String) variables.get("clickedKey");
  2385. Object itemId = itemIdMapper.get(key);
  2386. Object propertyId = null;
  2387. String colkey = (String) variables.get("clickedColKey");
  2388. // click is not necessary on a property
  2389. if (colkey != null) {
  2390. propertyId = columnIdMap.get(colkey);
  2391. }
  2392. MouseEventDetails evt = MouseEventDetails
  2393. .deSerialize((String) variables.get("clickEvent"));
  2394. Item item = getItem(itemId);
  2395. if (item != null) {
  2396. fireEvent(new ItemClickEvent(this, item, itemId, propertyId,
  2397. evt));
  2398. }
  2399. }
  2400. // Header click event
  2401. else if (variables.containsKey("headerClickEvent")) {
  2402. MouseEventDetails details = MouseEventDetails
  2403. .deSerialize((String) variables.get("headerClickEvent"));
  2404. Object cid = variables.get("headerClickCID");
  2405. Object propertyId = null;
  2406. if (cid != null) {
  2407. propertyId = columnIdMap.get(cid.toString());
  2408. }
  2409. fireEvent(new HeaderClickEvent(this, propertyId, details));
  2410. }
  2411. // Footer click event
  2412. else if (variables.containsKey("footerClickEvent")) {
  2413. MouseEventDetails details = MouseEventDetails
  2414. .deSerialize((String) variables.get("footerClickEvent"));
  2415. Object cid = variables.get("footerClickCID");
  2416. Object propertyId = null;
  2417. if (cid != null) {
  2418. propertyId = columnIdMap.get(cid.toString());
  2419. }
  2420. fireEvent(new FooterClickEvent(this, propertyId, details));
  2421. }
  2422. }
  2423. /**
  2424. * Handles the column resize event sent by the client.
  2425. *
  2426. * @param variables
  2427. */
  2428. private void handleColumnResizeEvent(Map<String, Object> variables) {
  2429. if (variables.containsKey("columnResizeEventColumn")) {
  2430. Object cid = variables.get("columnResizeEventColumn");
  2431. Object propertyId = null;
  2432. if (cid != null) {
  2433. propertyId = columnIdMap.get(cid.toString());
  2434. Object prev = variables.get("columnResizeEventPrev");
  2435. int previousWidth = -1;
  2436. if (prev != null) {
  2437. previousWidth = Integer.valueOf(prev.toString());
  2438. }
  2439. Object curr = variables.get("columnResizeEventCurr");
  2440. int currentWidth = -1;
  2441. if (curr != null) {
  2442. currentWidth = Integer.valueOf(curr.toString());
  2443. }
  2444. fireColumnResizeEvent(propertyId, previousWidth, currentWidth);
  2445. }
  2446. }
  2447. }
  2448. private void fireColumnResizeEvent(Object propertyId, int previousWidth,
  2449. int currentWidth) {
  2450. /*
  2451. * Update the sizes on the server side. If a column previously had a
  2452. * expand ratio and the user resized the column then the expand ratio
  2453. * will be turned into a static pixel size.
  2454. */
  2455. setColumnWidth(propertyId, currentWidth);
  2456. fireEvent(new ColumnResizeEvent(this, propertyId, previousWidth,
  2457. currentWidth));
  2458. }
  2459. private void handleColumnWidthUpdates(Map<String, Object> variables) {
  2460. if (variables.containsKey("columnWidthUpdates")) {
  2461. String[] events = (String[]) variables.get("columnWidthUpdates");
  2462. for (String str : events) {
  2463. String[] eventDetails = str.split(":");
  2464. Object propertyId = columnIdMap.get(eventDetails[0]);
  2465. if (propertyId == null) {
  2466. propertyId = ROW_HEADER_FAKE_PROPERTY_ID;
  2467. }
  2468. int width = Integer.valueOf(eventDetails[1]);
  2469. setColumnWidth(propertyId, width);
  2470. }
  2471. }
  2472. }
  2473. /**
  2474. * Go to mode where content updates are not done. This is due we want to
  2475. * bypass expensive content for some reason (like when we know we may have
  2476. * other content changes on their way).
  2477. *
  2478. * @return true if content refresh flag was enabled prior this call
  2479. */
  2480. protected boolean disableContentRefreshing() {
  2481. boolean wasDisabled = isContentRefreshesEnabled;
  2482. isContentRefreshesEnabled = false;
  2483. return wasDisabled;
  2484. }
  2485. /**
  2486. * Go to mode where content content refreshing has effect.
  2487. *
  2488. * @param refreshContent
  2489. * true if content refresh needs to be done
  2490. */
  2491. protected void enableContentRefreshing(boolean refreshContent) {
  2492. isContentRefreshesEnabled = true;
  2493. if (refreshContent) {
  2494. refreshRenderedCells();
  2495. // Ensure that client gets a response
  2496. requestRepaint();
  2497. }
  2498. }
  2499. /*
  2500. * (non-Javadoc)
  2501. *
  2502. * @see com.vaadin.ui.AbstractSelect#paintContent(com.vaadin.
  2503. * terminal.PaintTarget)
  2504. */
  2505. @Override
  2506. public void paintContent(PaintTarget target) throws PaintException {
  2507. /*
  2508. * Body actions - Actions which has the target null and can be invoked
  2509. * by right clicking on the table body.
  2510. */
  2511. final Set<Action> actionSet = findAndPaintBodyActions(target);
  2512. final Object[][] cells = getVisibleCells();
  2513. int rows = findNumRowsToPaint(target, cells);
  2514. int total = size();
  2515. if (shouldHideNullSelectionItem()) {
  2516. total--;
  2517. rows--;
  2518. }
  2519. // Table attributes
  2520. paintTableAttributes(target, rows, total);
  2521. paintVisibleColumnOrder(target);
  2522. // Rows
  2523. if (isPartialRowUpdate() && painted && !target.isFullRepaint()) {
  2524. paintPartialRowUpdate(target, actionSet);
  2525. /*
  2526. * Send the page buffer indexes to ensure that the client side stays
  2527. * in sync. Otherwise we _might_ have the situation where the client
  2528. * side discards too few or too many rows, causing out of sync
  2529. * issues.
  2530. *
  2531. * This could probably be done for full repaints also to simplify
  2532. * the client side.
  2533. */
  2534. int pageBufferLastIndex = pageBufferFirstIndex
  2535. + pageBuffer[CELL_ITEMID].length - 1;
  2536. target.addAttribute(VScrollTable.ATTRIBUTE_PAGEBUFFER_FIRST,
  2537. pageBufferFirstIndex);
  2538. target.addAttribute(VScrollTable.ATTRIBUTE_PAGEBUFFER_LAST,
  2539. pageBufferLastIndex);
  2540. } else if (target.isFullRepaint() || isRowCacheInvalidated()) {
  2541. paintRows(target, cells, actionSet);
  2542. setRowCacheInvalidated(false);
  2543. }
  2544. paintSorting(target);
  2545. resetVariablesAndPageBuffer(target);
  2546. // Actions
  2547. paintActions(target, actionSet);
  2548. paintColumnOrder(target);
  2549. // Available columns
  2550. paintAvailableColumns(target);
  2551. paintVisibleColumns(target);
  2552. if (dropHandler != null) {
  2553. dropHandler.getAcceptCriterion().paint(target);
  2554. }
  2555. painted = true;
  2556. }
  2557. private void setRowCacheInvalidated(boolean invalidated) {
  2558. rowCacheInvalidated = invalidated;
  2559. }
  2560. protected boolean isRowCacheInvalidated() {
  2561. return rowCacheInvalidated;
  2562. }
  2563. private void paintPartialRowUpdate(PaintTarget target, Set<Action> actionSet)
  2564. throws PaintException {
  2565. paintPartialRowUpdates(target, actionSet);
  2566. paintPartialRowAdditions(target, actionSet);
  2567. }
  2568. private void paintPartialRowUpdates(PaintTarget target,
  2569. Set<Action> actionSet) throws PaintException {
  2570. final boolean[] iscomponent = findCellsWithComponents();
  2571. int firstIx = getFirstUpdatedItemIndex();
  2572. int count = getUpdatedRowCount();
  2573. target.startTag("urows");
  2574. target.addAttribute("firsturowix", firstIx);
  2575. target.addAttribute("numurows", count);
  2576. // Partial row updates bypass the normal caching mechanism.
  2577. Object[][] cells = getVisibleCellsUpdateCacheRows(firstIx, count);
  2578. for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) {
  2579. final Object itemId = cells[CELL_ITEMID][indexInRowbuffer];
  2580. if (shouldHideNullSelectionItem()) {
  2581. // Remove null selection item if null selection is not allowed
  2582. continue;
  2583. }
  2584. paintRow(target, cells, isEditable(), actionSet, iscomponent,
  2585. indexInRowbuffer, itemId);
  2586. }
  2587. target.endTag("urows");
  2588. }
  2589. private void paintPartialRowAdditions(PaintTarget target,
  2590. Set<Action> actionSet) throws PaintException {
  2591. final boolean[] iscomponent = findCellsWithComponents();
  2592. int firstIx = getFirstAddedItemIndex();
  2593. int count = getAddedRowCount();
  2594. target.startTag("prows");
  2595. if (!shouldHideAddedRows()) {
  2596. getLogger().finest(
  2597. "Paint rows for add. Index: " + firstIx + ", count: "
  2598. + count + ".");
  2599. // Partial row additions bypass the normal caching mechanism.
  2600. Object[][] cells = getVisibleCellsInsertIntoCache(firstIx, count);
  2601. if (cells[0].length < count) {
  2602. // delete the rows below, since they will fall beyond the cache
  2603. // page.
  2604. target.addAttribute("delbelow", true);
  2605. count = cells[0].length;
  2606. }
  2607. for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) {
  2608. final Object itemId = cells[CELL_ITEMID][indexInRowbuffer];
  2609. if (shouldHideNullSelectionItem()) {
  2610. // Remove null selection item if null selection is not
  2611. // allowed
  2612. continue;
  2613. }
  2614. paintRow(target, cells, isEditable(), actionSet, iscomponent,
  2615. indexInRowbuffer, itemId);
  2616. }
  2617. } else {
  2618. getLogger().finest(
  2619. "Paint rows for remove. Index: " + firstIx + ", count: "
  2620. + count + ".");
  2621. removeRowsFromCacheAndFillBottom(firstIx, count);
  2622. target.addAttribute("hide", true);
  2623. }
  2624. target.addAttribute("firstprowix", firstIx);
  2625. target.addAttribute("numprows", count);
  2626. target.endTag("prows");
  2627. }
  2628. /**
  2629. * Subclass and override this to enable partial row updates and additions,
  2630. * which bypass the normal caching mechanism. This is useful for e.g.
  2631. * TreeTable.
  2632. *
  2633. * @return true if this update is a partial row update, false if not. For
  2634. * plain Table it is always false.
  2635. */
  2636. protected boolean isPartialRowUpdate() {
  2637. return false;
  2638. }
  2639. /**
  2640. * Subclass and override this to enable partial row additions, bypassing the
  2641. * normal caching mechanism. This is useful for e.g. TreeTable, where
  2642. * expanding a node should only fetch and add the items inside of that node.
  2643. *
  2644. * @return The index of the first added item. For plain Table it is always
  2645. * 0.
  2646. */
  2647. protected int getFirstAddedItemIndex() {
  2648. return 0;
  2649. }
  2650. /**
  2651. * Subclass and override this to enable partial row additions, bypassing the
  2652. * normal caching mechanism. This is useful for e.g. TreeTable, where
  2653. * expanding a node should only fetch and add the items inside of that node.
  2654. *
  2655. * @return the number of rows to be added, starting at the index returned by
  2656. * {@link #getFirstAddedItemIndex()}. For plain Table it is always
  2657. * 0.
  2658. */
  2659. protected int getAddedRowCount() {
  2660. return 0;
  2661. }
  2662. /**
  2663. * Subclass and override this to enable removing of rows, bypassing the
  2664. * normal caching and lazy loading mechanism. This is useful for e.g.
  2665. * TreeTable, when you need to hide certain rows as a node is collapsed.
  2666. *
  2667. * This should return true if the rows pointed to by
  2668. * {@link #getFirstAddedItemIndex()} and {@link #getAddedRowCount()} should
  2669. * be hidden instead of added.
  2670. *
  2671. * @return whether the rows to add (see {@link #getFirstAddedItemIndex()}
  2672. * and {@link #getAddedRowCount()}) should be added or hidden. For
  2673. * plain Table it is always false.
  2674. */
  2675. protected boolean shouldHideAddedRows() {
  2676. return false;
  2677. }
  2678. /**
  2679. * Subclass and override this to enable partial row updates, bypassing the
  2680. * normal caching and lazy loading mechanism. This is useful for updating
  2681. * the state of certain rows, e.g. in the TreeTable the collapsed state of a
  2682. * single node is updated using this mechanism.
  2683. *
  2684. * @return the index of the first item to be updated. For plain Table it is
  2685. * always 0.
  2686. */
  2687. protected int getFirstUpdatedItemIndex() {
  2688. return 0;
  2689. }
  2690. /**
  2691. * Subclass and override this to enable partial row updates, bypassing the
  2692. * normal caching and lazy loading mechanism. This is useful for updating
  2693. * the state of certain rows, e.g. in the TreeTable the collapsed state of a
  2694. * single node is updated using this mechanism.
  2695. *
  2696. * @return the number of rows to update, starting at the index returned by
  2697. * {@link #getFirstUpdatedItemIndex()}. For plain table it is always
  2698. * 0.
  2699. */
  2700. protected int getUpdatedRowCount() {
  2701. return 0;
  2702. }
  2703. private void paintTableAttributes(PaintTarget target, int rows, int total)
  2704. throws PaintException {
  2705. paintTabIndex(target);
  2706. paintDragMode(target);
  2707. paintSelectMode(target);
  2708. if (cacheRate != CACHE_RATE_DEFAULT) {
  2709. target.addAttribute("cr", cacheRate);
  2710. }
  2711. target.addAttribute("cols", getVisibleColumns().length);
  2712. target.addAttribute("rows", rows);
  2713. target.addAttribute("firstrow",
  2714. (reqFirstRowToPaint >= 0 ? reqFirstRowToPaint
  2715. : firstToBeRenderedInClient));
  2716. target.addAttribute("totalrows", total);
  2717. if (getPageLength() != 0) {
  2718. target.addAttribute("pagelength", getPageLength());
  2719. }
  2720. if (areColumnHeadersEnabled()) {
  2721. target.addAttribute("colheaders", true);
  2722. }
  2723. if (rowHeadersAreEnabled()) {
  2724. target.addAttribute("rowheaders", true);
  2725. }
  2726. target.addAttribute("colfooters", columnFootersVisible);
  2727. // The cursors are only shown on pageable table
  2728. if (getCurrentPageFirstItemIndex() != 0 || getPageLength() > 0) {
  2729. target.addVariable(this, "firstvisible",
  2730. getCurrentPageFirstItemIndex());
  2731. }
  2732. }
  2733. /**
  2734. * Resets and paints "to be painted next" variables. Also reset pageBuffer
  2735. */
  2736. private void resetVariablesAndPageBuffer(PaintTarget target)
  2737. throws PaintException {
  2738. reqFirstRowToPaint = -1;
  2739. reqRowsToPaint = -1;
  2740. containerChangeToBeRendered = false;
  2741. target.addVariable(this, "reqrows", reqRowsToPaint);
  2742. target.addVariable(this, "reqfirstrow", reqFirstRowToPaint);
  2743. }
  2744. private boolean areColumnHeadersEnabled() {
  2745. return getColumnHeaderMode() != ColumnHeaderMode.HIDDEN;
  2746. }
  2747. private void paintVisibleColumns(PaintTarget target) throws PaintException {
  2748. target.startTag("visiblecolumns");
  2749. if (rowHeadersAreEnabled()) {
  2750. target.startTag("column");
  2751. target.addAttribute("cid", ROW_HEADER_COLUMN_KEY);
  2752. paintColumnWidth(target, ROW_HEADER_FAKE_PROPERTY_ID);
  2753. target.endTag("column");
  2754. }
  2755. final Collection<?> sortables = getSortableContainerPropertyIds();
  2756. for (Object colId : visibleColumns) {
  2757. if (colId != null) {
  2758. target.startTag("column");
  2759. target.addAttribute("cid", columnIdMap.key(colId));
  2760. final String head = getColumnHeader(colId);
  2761. target.addAttribute("caption", (head != null ? head : ""));
  2762. final String foot = getColumnFooter(colId);
  2763. target.addAttribute("fcaption", (foot != null ? foot : ""));
  2764. if (isColumnCollapsed(colId)) {
  2765. target.addAttribute("collapsed", true);
  2766. }
  2767. if (areColumnHeadersEnabled()) {
  2768. if (getColumnIcon(colId) != null) {
  2769. target.addAttribute("icon", getColumnIcon(colId));
  2770. }
  2771. if (sortables.contains(colId)) {
  2772. target.addAttribute("sortable", true);
  2773. }
  2774. }
  2775. if (!Align.LEFT.equals(getColumnAlignment(colId))) {
  2776. target.addAttribute("align", getColumnAlignment(colId)
  2777. .toString());
  2778. }
  2779. paintColumnWidth(target, colId);
  2780. target.endTag("column");
  2781. }
  2782. }
  2783. target.endTag("visiblecolumns");
  2784. }
  2785. private void paintAvailableColumns(PaintTarget target)
  2786. throws PaintException {
  2787. if (columnCollapsingAllowed) {
  2788. final HashSet<Object> collapsedCols = new HashSet<Object>();
  2789. for (Object colId : visibleColumns) {
  2790. if (isColumnCollapsed(colId)) {
  2791. collapsedCols.add(colId);
  2792. }
  2793. }
  2794. final String[] collapsedKeys = new String[collapsedCols.size()];
  2795. int nextColumn = 0;
  2796. for (Object colId : visibleColumns) {
  2797. if (isColumnCollapsed(colId)) {
  2798. collapsedKeys[nextColumn++] = columnIdMap.key(colId);
  2799. }
  2800. }
  2801. target.addVariable(this, "collapsedcolumns", collapsedKeys);
  2802. final String[] noncollapsibleKeys = new String[noncollapsibleColumns
  2803. .size()];
  2804. nextColumn = 0;
  2805. for (Object colId : noncollapsibleColumns) {
  2806. noncollapsibleKeys[nextColumn++] = columnIdMap.key(colId);
  2807. }
  2808. target.addVariable(this, "noncollapsiblecolumns",
  2809. noncollapsibleKeys);
  2810. }
  2811. }
  2812. private void paintActions(PaintTarget target, final Set<Action> actionSet)
  2813. throws PaintException {
  2814. if (!actionSet.isEmpty()) {
  2815. target.addVariable(this, "action", "");
  2816. target.startTag("actions");
  2817. for (Action a : actionSet) {
  2818. target.startTag("action");
  2819. if (a.getCaption() != null) {
  2820. target.addAttribute("caption", a.getCaption());
  2821. }
  2822. if (a.getIcon() != null) {
  2823. target.addAttribute("icon", a.getIcon());
  2824. }
  2825. target.addAttribute("key", actionMapper.key(a));
  2826. target.endTag("action");
  2827. }
  2828. target.endTag("actions");
  2829. }
  2830. }
  2831. private void paintColumnOrder(PaintTarget target) throws PaintException {
  2832. if (columnReorderingAllowed) {
  2833. final String[] colorder = new String[visibleColumns.size()];
  2834. int i = 0;
  2835. for (Object colId : visibleColumns) {
  2836. colorder[i++] = columnIdMap.key(colId);
  2837. }
  2838. target.addVariable(this, "columnorder", colorder);
  2839. }
  2840. }
  2841. private void paintSorting(PaintTarget target) throws PaintException {
  2842. // Sorting
  2843. if (getContainerDataSource() instanceof Container.Sortable) {
  2844. target.addVariable(this, "sortcolumn",
  2845. columnIdMap.key(sortContainerPropertyId));
  2846. target.addVariable(this, "sortascending", sortAscending);
  2847. }
  2848. }
  2849. private void paintRows(PaintTarget target, final Object[][] cells,
  2850. final Set<Action> actionSet) throws PaintException {
  2851. final boolean[] iscomponent = findCellsWithComponents();
  2852. target.startTag("rows");
  2853. // cells array contains all that are supposed to be visible on client,
  2854. // but we'll start from the one requested by client
  2855. int start = 0;
  2856. if (reqFirstRowToPaint != -1 && firstToBeRenderedInClient != -1) {
  2857. start = reqFirstRowToPaint - firstToBeRenderedInClient;
  2858. }
  2859. int end = cells[0].length;
  2860. if (reqRowsToPaint != -1) {
  2861. end = start + reqRowsToPaint;
  2862. }
  2863. // sanity check
  2864. if (lastToBeRenderedInClient != -1 && lastToBeRenderedInClient < end) {
  2865. end = lastToBeRenderedInClient + 1;
  2866. }
  2867. if (start > cells[CELL_ITEMID].length || start < 0) {
  2868. start = 0;
  2869. }
  2870. if (end > cells[CELL_ITEMID].length) {
  2871. end = cells[CELL_ITEMID].length;
  2872. }
  2873. for (int indexInRowbuffer = start; indexInRowbuffer < end; indexInRowbuffer++) {
  2874. final Object itemId = cells[CELL_ITEMID][indexInRowbuffer];
  2875. if (shouldHideNullSelectionItem()) {
  2876. // Remove null selection item if null selection is not allowed
  2877. continue;
  2878. }
  2879. paintRow(target, cells, isEditable(), actionSet, iscomponent,
  2880. indexInRowbuffer, itemId);
  2881. }
  2882. target.endTag("rows");
  2883. }
  2884. private boolean[] findCellsWithComponents() {
  2885. final boolean[] isComponent = new boolean[visibleColumns.size()];
  2886. int ix = 0;
  2887. for (Object columnId : visibleColumns) {
  2888. if (columnGenerators.containsKey(columnId)) {
  2889. isComponent[ix++] = true;
  2890. } else {
  2891. final Class<?> colType = getType(columnId);
  2892. isComponent[ix++] = colType != null
  2893. && Component.class.isAssignableFrom(colType);
  2894. }
  2895. }
  2896. return isComponent;
  2897. }
  2898. private void paintVisibleColumnOrder(PaintTarget target) {
  2899. // Visible column order
  2900. final ArrayList<String> visibleColOrder = new ArrayList<String>();
  2901. for (Object columnId : visibleColumns) {
  2902. if (!isColumnCollapsed(columnId)) {
  2903. visibleColOrder.add(columnIdMap.key(columnId));
  2904. }
  2905. }
  2906. target.addAttribute("vcolorder", visibleColOrder.toArray());
  2907. }
  2908. private Set<Action> findAndPaintBodyActions(PaintTarget target) {
  2909. Set<Action> actionSet = new LinkedHashSet<Action>();
  2910. if (actionHandlers != null) {
  2911. final ArrayList<String> keys = new ArrayList<String>();
  2912. for (Handler ah : actionHandlers) {
  2913. // Getting actions for the null item, which in this case means
  2914. // the body item
  2915. final Action[] actions = ah.getActions(null, this);
  2916. if (actions != null) {
  2917. for (Action action : actions) {
  2918. actionSet.add(action);
  2919. keys.add(actionMapper.key(action));
  2920. }
  2921. }
  2922. }
  2923. target.addAttribute("alb", keys.toArray());
  2924. }
  2925. return actionSet;
  2926. }
  2927. private boolean shouldHideNullSelectionItem() {
  2928. return !isNullSelectionAllowed() && getNullSelectionItemId() != null
  2929. && containsId(getNullSelectionItemId());
  2930. }
  2931. private int findNumRowsToPaint(PaintTarget target, final Object[][] cells)
  2932. throws PaintException {
  2933. int rows;
  2934. if (reqRowsToPaint >= 0) {
  2935. rows = reqRowsToPaint;
  2936. } else {
  2937. rows = cells[0].length;
  2938. if (alwaysRecalculateColumnWidths) {
  2939. // TODO experimental feature for now: tell the client to
  2940. // recalculate column widths.
  2941. // We'll only do this for paints that do not originate from
  2942. // table scroll/cache requests (i.e when reqRowsToPaint<0)
  2943. target.addAttribute("recalcWidths", true);
  2944. }
  2945. }
  2946. return rows;
  2947. }
  2948. private void paintSelectMode(PaintTarget target) throws PaintException {
  2949. if (multiSelectMode != MultiSelectMode.DEFAULT) {
  2950. target.addAttribute("multiselectmode", multiSelectMode.ordinal());
  2951. }
  2952. if (isSelectable()) {
  2953. target.addAttribute("selectmode", (isMultiSelect() ? "multi"
  2954. : "single"));
  2955. } else {
  2956. target.addAttribute("selectmode", "none");
  2957. }
  2958. if (!isNullSelectionAllowed()) {
  2959. target.addAttribute("nsa", false);
  2960. }
  2961. // selection support
  2962. // The select variable is only enabled if selectable
  2963. if (isSelectable()) {
  2964. target.addVariable(this, "selected", findSelectedKeys());
  2965. }
  2966. }
  2967. private String[] findSelectedKeys() {
  2968. LinkedList<String> selectedKeys = new LinkedList<String>();
  2969. if (isMultiSelect()) {
  2970. HashSet<?> sel = new HashSet<Object>((Set<?>) getValue());
  2971. Collection<?> vids = getVisibleItemIds();
  2972. for (Iterator<?> it = vids.iterator(); it.hasNext();) {
  2973. Object id = it.next();
  2974. if (sel.contains(id)) {
  2975. selectedKeys.add(itemIdMapper.key(id));
  2976. }
  2977. }
  2978. } else {
  2979. Object value = getValue();
  2980. if (value == null) {
  2981. value = getNullSelectionItemId();
  2982. }
  2983. if (value != null) {
  2984. selectedKeys.add(itemIdMapper.key(value));
  2985. }
  2986. }
  2987. return selectedKeys.toArray(new String[selectedKeys.size()]);
  2988. }
  2989. private void paintDragMode(PaintTarget target) throws PaintException {
  2990. if (dragMode != TableDragMode.NONE) {
  2991. target.addAttribute("dragmode", dragMode.ordinal());
  2992. }
  2993. }
  2994. private void paintTabIndex(PaintTarget target) throws PaintException {
  2995. // The tab ordering number
  2996. if (getTabIndex() > 0) {
  2997. target.addAttribute("tabindex", getTabIndex());
  2998. }
  2999. }
  3000. private void paintColumnWidth(PaintTarget target, final Object columnId)
  3001. throws PaintException {
  3002. if (columnWidths.containsKey(columnId)) {
  3003. if (getColumnWidth(columnId) > -1) {
  3004. target.addAttribute("width",
  3005. String.valueOf(getColumnWidth(columnId)));
  3006. } else {
  3007. target.addAttribute("er", getColumnExpandRatio(columnId));
  3008. }
  3009. }
  3010. }
  3011. private boolean rowHeadersAreEnabled() {
  3012. return getRowHeaderMode() != ROW_HEADER_MODE_HIDDEN;
  3013. }
  3014. private void paintRow(PaintTarget target, final Object[][] cells,
  3015. final boolean iseditable, final Set<Action> actionSet,
  3016. final boolean[] iscomponent, int indexInRowbuffer,
  3017. final Object itemId) throws PaintException {
  3018. target.startTag("tr");
  3019. paintRowAttributes(target, cells, actionSet, indexInRowbuffer, itemId);
  3020. // cells
  3021. int currentColumn = 0;
  3022. for (final Iterator<Object> it = visibleColumns.iterator(); it
  3023. .hasNext(); currentColumn++) {
  3024. final Object columnId = it.next();
  3025. if (columnId == null || isColumnCollapsed(columnId)) {
  3026. continue;
  3027. }
  3028. /*
  3029. * For each cell, if a cellStyleGenerator is specified, get the
  3030. * specific style for the cell. If there is any, add it to the
  3031. * target.
  3032. */
  3033. if (cellStyleGenerator != null) {
  3034. String cellStyle = cellStyleGenerator
  3035. .getStyle(itemId, columnId);
  3036. if (cellStyle != null && !cellStyle.equals("")) {
  3037. target.addAttribute("style-" + columnIdMap.key(columnId),
  3038. cellStyle);
  3039. }
  3040. }
  3041. if ((iscomponent[currentColumn] || iseditable || cells[CELL_GENERATED_ROW][indexInRowbuffer] != null)
  3042. && Component.class.isInstance(cells[CELL_FIRSTCOL
  3043. + currentColumn][indexInRowbuffer])) {
  3044. final Component c = (Component) cells[CELL_FIRSTCOL
  3045. + currentColumn][indexInRowbuffer];
  3046. if (c == null) {
  3047. target.addText("");
  3048. paintCellTooltips(target, itemId, columnId);
  3049. } else {
  3050. LegacyPaint.paint(c, target);
  3051. }
  3052. } else {
  3053. target.addText((String) cells[CELL_FIRSTCOL + currentColumn][indexInRowbuffer]);
  3054. paintCellTooltips(target, itemId, columnId);
  3055. }
  3056. }
  3057. target.endTag("tr");
  3058. }
  3059. private void paintCellTooltips(PaintTarget target, Object itemId,
  3060. Object columnId) throws PaintException {
  3061. if (itemDescriptionGenerator != null) {
  3062. String itemDescription = itemDescriptionGenerator
  3063. .generateDescription(this, itemId, columnId);
  3064. if (itemDescription != null && !itemDescription.equals("")) {
  3065. target.addAttribute("descr-" + columnIdMap.key(columnId),
  3066. itemDescription);
  3067. }
  3068. }
  3069. }
  3070. private void paintRowTooltips(PaintTarget target, Object itemId)
  3071. throws PaintException {
  3072. if (itemDescriptionGenerator != null) {
  3073. String rowDescription = itemDescriptionGenerator
  3074. .generateDescription(this, itemId, null);
  3075. if (rowDescription != null && !rowDescription.equals("")) {
  3076. target.addAttribute("rowdescr", rowDescription);
  3077. }
  3078. }
  3079. }
  3080. private void paintRowAttributes(PaintTarget target, final Object[][] cells,
  3081. final Set<Action> actionSet, int indexInRowbuffer,
  3082. final Object itemId) throws PaintException {
  3083. // tr attributes
  3084. paintRowIcon(target, cells, indexInRowbuffer);
  3085. paintRowHeader(target, cells, indexInRowbuffer);
  3086. paintGeneratedRowInfo(target, cells, indexInRowbuffer);
  3087. target.addAttribute("key",
  3088. Integer.parseInt(cells[CELL_KEY][indexInRowbuffer].toString()));
  3089. if (isSelected(itemId)) {
  3090. target.addAttribute("selected", true);
  3091. }
  3092. // Actions
  3093. if (actionHandlers != null) {
  3094. final ArrayList<String> keys = new ArrayList<String>();
  3095. for (Handler ah : actionHandlers) {
  3096. final Action[] aa = ah.getActions(itemId, this);
  3097. if (aa != null) {
  3098. for (int ai = 0; ai < aa.length; ai++) {
  3099. final String key = actionMapper.key(aa[ai]);
  3100. actionSet.add(aa[ai]);
  3101. keys.add(key);
  3102. }
  3103. }
  3104. }
  3105. target.addAttribute("al", keys.toArray());
  3106. }
  3107. /*
  3108. * For each row, if a cellStyleGenerator is specified, get the specific
  3109. * style for the cell, using null as propertyId. If there is any, add it
  3110. * to the target.
  3111. */
  3112. if (cellStyleGenerator != null) {
  3113. String rowStyle = cellStyleGenerator.getStyle(itemId, null);
  3114. if (rowStyle != null && !rowStyle.equals("")) {
  3115. target.addAttribute("rowstyle", rowStyle);
  3116. }
  3117. }
  3118. paintRowTooltips(target, itemId);
  3119. paintRowAttributes(target, itemId);
  3120. }
  3121. private void paintGeneratedRowInfo(PaintTarget target, Object[][] cells,
  3122. int indexInRowBuffer) throws PaintException {
  3123. GeneratedRow generatedRow = (GeneratedRow) cells[CELL_GENERATED_ROW][indexInRowBuffer];
  3124. if (generatedRow != null) {
  3125. target.addAttribute("gen_html", generatedRow.isHtmlContentAllowed());
  3126. target.addAttribute("gen_span", generatedRow.isSpanColumns());
  3127. target.addAttribute("gen_widget",
  3128. generatedRow.getValue() instanceof Component);
  3129. }
  3130. }
  3131. protected void paintRowHeader(PaintTarget target, Object[][] cells,
  3132. int indexInRowbuffer) throws PaintException {
  3133. if (rowHeadersAreEnabled()) {
  3134. if (cells[CELL_HEADER][indexInRowbuffer] != null) {
  3135. target.addAttribute("caption",
  3136. (String) cells[CELL_HEADER][indexInRowbuffer]);
  3137. }
  3138. }
  3139. }
  3140. protected void paintRowIcon(PaintTarget target, final Object[][] cells,
  3141. int indexInRowbuffer) throws PaintException {
  3142. if (rowHeadersAreEnabled()
  3143. && cells[CELL_ICON][indexInRowbuffer] != null) {
  3144. target.addAttribute("icon",
  3145. (Resource) cells[CELL_ICON][indexInRowbuffer]);
  3146. }
  3147. }
  3148. /**
  3149. * A method where extended Table implementations may add their custom
  3150. * attributes for rows.
  3151. *
  3152. * @param target
  3153. * @param itemId
  3154. */
  3155. protected void paintRowAttributes(PaintTarget target, Object itemId)
  3156. throws PaintException {
  3157. }
  3158. /**
  3159. * Gets the cached visible table contents.
  3160. *
  3161. * @return the cached visible table contents.
  3162. */
  3163. private Object[][] getVisibleCells() {
  3164. if (pageBuffer == null) {
  3165. refreshRenderedCells();
  3166. }
  3167. return pageBuffer;
  3168. }
  3169. /**
  3170. * Gets the value of property.
  3171. *
  3172. * By default if the table is editable the fieldFactory is used to create
  3173. * editors for table cells. Otherwise formatPropertyValue is used to format
  3174. * the value representation.
  3175. *
  3176. * @param rowId
  3177. * the Id of the row (same as item Id).
  3178. * @param colId
  3179. * the Id of the column.
  3180. * @param property
  3181. * the Property to be presented.
  3182. * @return Object Either formatted value or Component for field.
  3183. * @see #setTableFieldFactory(TableFieldFactory)
  3184. */
  3185. protected Object getPropertyValue(Object rowId, Object colId,
  3186. Property property) {
  3187. if (isEditable() && fieldFactory != null) {
  3188. final Field<?> f = fieldFactory.createField(
  3189. getContainerDataSource(), rowId, colId, this);
  3190. if (f != null) {
  3191. // Remember that we have made this association so we can remove
  3192. // it when the component is removed
  3193. associatedProperties.put(f, property);
  3194. bindPropertyToField(rowId, colId, property, f);
  3195. return f;
  3196. }
  3197. }
  3198. return formatPropertyValue(rowId, colId, property);
  3199. }
  3200. /**
  3201. * Binds an item property to a field generated by TableFieldFactory. The
  3202. * default behavior is to bind property straight to Field. If
  3203. * Property.Viewer type property (e.g. PropertyFormatter) is already set for
  3204. * field, the property is bound to that Property.Viewer.
  3205. *
  3206. * @param rowId
  3207. * @param colId
  3208. * @param property
  3209. * @param field
  3210. * @since 6.7.3
  3211. */
  3212. protected void bindPropertyToField(Object rowId, Object colId,
  3213. Property property, Field field) {
  3214. // check if field has a property that is Viewer set. In that case we
  3215. // expect developer has e.g. PropertyFormatter that he wishes to use and
  3216. // assign the property to the Viewer instead.
  3217. boolean hasFilterProperty = field.getPropertyDataSource() != null
  3218. && (field.getPropertyDataSource() instanceof Property.Viewer);
  3219. if (hasFilterProperty) {
  3220. ((Property.Viewer) field.getPropertyDataSource())
  3221. .setPropertyDataSource(property);
  3222. } else {
  3223. field.setPropertyDataSource(property);
  3224. }
  3225. }
  3226. /**
  3227. * Formats table cell property values. By default the property.toString()
  3228. * and return a empty string for null properties.
  3229. *
  3230. * @param rowId
  3231. * the Id of the row (same as item Id).
  3232. * @param colId
  3233. * the Id of the column.
  3234. * @param property
  3235. * the Property to be formatted.
  3236. * @return the String representation of property and its value.
  3237. * @since 3.1
  3238. */
  3239. protected String formatPropertyValue(Object rowId, Object colId,
  3240. Property<?> property) {
  3241. if (property == null) {
  3242. return "";
  3243. }
  3244. Converter<String, Object> converter = null;
  3245. if (hasConverter(colId)) {
  3246. converter = getConverter(colId);
  3247. } else {
  3248. Application app = Application.getCurrentApplication();
  3249. if (app != null) {
  3250. converter = (Converter<String, Object>) app
  3251. .getConverterFactory().createConverter(String.class,
  3252. property.getType());
  3253. }
  3254. }
  3255. Object value = property.getValue();
  3256. if (converter != null) {
  3257. return converter.convertToPresentation(value, getLocale());
  3258. }
  3259. return (null != value) ? value.toString() : "";
  3260. }
  3261. /* Action container */
  3262. /**
  3263. * Registers a new action handler for this container
  3264. *
  3265. * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler)
  3266. */
  3267. public void addActionHandler(Action.Handler actionHandler) {
  3268. if (actionHandler != null) {
  3269. if (actionHandlers == null) {
  3270. actionHandlers = new LinkedList<Handler>();
  3271. actionMapper = new KeyMapper<Action>();
  3272. }
  3273. if (!actionHandlers.contains(actionHandler)) {
  3274. actionHandlers.add(actionHandler);
  3275. // Assures the visual refresh. No need to reset the page buffer
  3276. // before as the content has not changed, only the action
  3277. // handlers.
  3278. refreshRenderedCells();
  3279. }
  3280. }
  3281. }
  3282. /**
  3283. * Removes a previously registered action handler for the contents of this
  3284. * container.
  3285. *
  3286. * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler)
  3287. */
  3288. public void removeActionHandler(Action.Handler actionHandler) {
  3289. if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
  3290. actionHandlers.remove(actionHandler);
  3291. if (actionHandlers.isEmpty()) {
  3292. actionHandlers = null;
  3293. actionMapper = null;
  3294. }
  3295. // Assures the visual refresh. No need to reset the page buffer
  3296. // before as the content has not changed, only the action
  3297. // handlers.
  3298. refreshRenderedCells();
  3299. }
  3300. }
  3301. /**
  3302. * Removes all action handlers
  3303. */
  3304. public void removeAllActionHandlers() {
  3305. actionHandlers = null;
  3306. actionMapper = null;
  3307. // Assures the visual refresh. No need to reset the page buffer
  3308. // before as the content has not changed, only the action
  3309. // handlers.
  3310. refreshRenderedCells();
  3311. }
  3312. /* Property value change listening support */
  3313. /**
  3314. * Notifies this listener that the Property's value has changed.
  3315. *
  3316. * Also listens changes in rendered items to refresh content area.
  3317. *
  3318. * @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent)
  3319. */
  3320. @Override
  3321. public void valueChange(Property.ValueChangeEvent event) {
  3322. if (event.getProperty() == this
  3323. || event.getProperty() == getPropertyDataSource()) {
  3324. super.valueChange(event);
  3325. } else {
  3326. refreshRowCache();
  3327. containerChangeToBeRendered = true;
  3328. }
  3329. requestRepaint();
  3330. }
  3331. /**
  3332. * Clears the current page buffer. Call this before
  3333. * {@link #refreshRenderedCells()} to ensure that all content is updated
  3334. * from the properties.
  3335. */
  3336. protected void resetPageBuffer() {
  3337. firstToBeRenderedInClient = -1;
  3338. lastToBeRenderedInClient = -1;
  3339. reqFirstRowToPaint = -1;
  3340. reqRowsToPaint = -1;
  3341. pageBuffer = null;
  3342. }
  3343. /**
  3344. * Notifies the component that it is connected to an application.
  3345. *
  3346. * @see com.vaadin.ui.Component#attach()
  3347. */
  3348. @Override
  3349. public void attach() {
  3350. super.attach();
  3351. refreshRenderedCells();
  3352. }
  3353. /**
  3354. * Notifies the component that it is detached from the application
  3355. *
  3356. * @see com.vaadin.ui.Component#detach()
  3357. */
  3358. @Override
  3359. public void detach() {
  3360. super.detach();
  3361. }
  3362. /**
  3363. * Removes all Items from the Container.
  3364. *
  3365. * @see com.vaadin.data.Container#removeAllItems()
  3366. */
  3367. @Override
  3368. public boolean removeAllItems() {
  3369. currentPageFirstItemId = null;
  3370. currentPageFirstItemIndex = 0;
  3371. return super.removeAllItems();
  3372. }
  3373. /**
  3374. * Removes the Item identified by <code>ItemId</code> from the Container.
  3375. *
  3376. * @see com.vaadin.data.Container#removeItem(Object)
  3377. */
  3378. @Override
  3379. public boolean removeItem(Object itemId) {
  3380. final Object nextItemId = nextItemId(itemId);
  3381. final boolean ret = super.removeItem(itemId);
  3382. if (ret && (itemId != null) && (itemId.equals(currentPageFirstItemId))) {
  3383. currentPageFirstItemId = nextItemId;
  3384. }
  3385. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  3386. refreshRowCache();
  3387. }
  3388. return ret;
  3389. }
  3390. /**
  3391. * Removes a Property specified by the given Property ID from the Container.
  3392. *
  3393. * @see com.vaadin.data.Container#removeContainerProperty(Object)
  3394. */
  3395. @Override
  3396. public boolean removeContainerProperty(Object propertyId)
  3397. throws UnsupportedOperationException {
  3398. // If a visible property is removed, remove the corresponding column
  3399. visibleColumns.remove(propertyId);
  3400. columnAlignments.remove(propertyId);
  3401. columnIcons.remove(propertyId);
  3402. columnHeaders.remove(propertyId);
  3403. columnFooters.remove(propertyId);
  3404. return super.removeContainerProperty(propertyId);
  3405. }
  3406. /**
  3407. * Adds a new property to the table and show it as a visible column.
  3408. *
  3409. * @param propertyId
  3410. * the Id of the proprty.
  3411. * @param type
  3412. * the class of the property.
  3413. * @param defaultValue
  3414. * the default value given for all existing items.
  3415. * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
  3416. * Object)
  3417. */
  3418. @Override
  3419. public boolean addContainerProperty(Object propertyId, Class<?> type,
  3420. Object defaultValue) throws UnsupportedOperationException {
  3421. boolean visibleColAdded = false;
  3422. if (!visibleColumns.contains(propertyId)) {
  3423. visibleColumns.add(propertyId);
  3424. visibleColAdded = true;
  3425. }
  3426. if (!super.addContainerProperty(propertyId, type, defaultValue)) {
  3427. if (visibleColAdded) {
  3428. visibleColumns.remove(propertyId);
  3429. }
  3430. return false;
  3431. }
  3432. if (!(items instanceof Container.PropertySetChangeNotifier)) {
  3433. refreshRowCache();
  3434. }
  3435. return true;
  3436. }
  3437. /**
  3438. * Adds a new property to the table and show it as a visible column.
  3439. *
  3440. * @param propertyId
  3441. * the Id of the proprty
  3442. * @param type
  3443. * the class of the property
  3444. * @param defaultValue
  3445. * the default value given for all existing items
  3446. * @param columnHeader
  3447. * the Explicit header of the column. If explicit header is not
  3448. * needed, this should be set null.
  3449. * @param columnIcon
  3450. * the Icon of the column. If icon is not needed, this should be
  3451. * set null.
  3452. * @param columnAlignment
  3453. * the Alignment of the column. Null implies align left.
  3454. * @throws UnsupportedOperationException
  3455. * if the operation is not supported.
  3456. * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
  3457. * Object)
  3458. */
  3459. public boolean addContainerProperty(Object propertyId, Class<?> type,
  3460. Object defaultValue, String columnHeader, Resource columnIcon,
  3461. Align columnAlignment) throws UnsupportedOperationException {
  3462. if (!this.addContainerProperty(propertyId, type, defaultValue)) {
  3463. return false;
  3464. }
  3465. setColumnAlignment(propertyId, columnAlignment);
  3466. setColumnHeader(propertyId, columnHeader);
  3467. setColumnIcon(propertyId, columnIcon);
  3468. return true;
  3469. }
  3470. /**
  3471. * Adds a generated column to the Table.
  3472. * <p>
  3473. * A generated column is a column that exists only in the Table, not as a
  3474. * property in the underlying Container. It shows up just as a regular
  3475. * column.
  3476. * </p>
  3477. * <p>
  3478. * A generated column will override a property with the same id, so that the
  3479. * generated column is shown instead of the column representing the
  3480. * property. Note that getContainerProperty() will still get the real
  3481. * property.
  3482. * </p>
  3483. * <p>
  3484. * Table will not listen to value change events from properties overridden
  3485. * by generated columns. If the content of your generated column depends on
  3486. * properties that are not directly visible in the table, attach value
  3487. * change listener to update the content on all depended properties.
  3488. * Otherwise your UI might not get updated as expected.
  3489. * </p>
  3490. * <p>
  3491. * Also note that getVisibleColumns() will return the generated columns,
  3492. * while getContainerPropertyIds() will not.
  3493. * </p>
  3494. *
  3495. * @param id
  3496. * the id of the column to be added
  3497. * @param generatedColumn
  3498. * the {@link ColumnGenerator} to use for this column
  3499. */
  3500. public void addGeneratedColumn(Object id, ColumnGenerator generatedColumn) {
  3501. if (generatedColumn == null) {
  3502. throw new IllegalArgumentException(
  3503. "Can not add null as a GeneratedColumn");
  3504. }
  3505. if (columnGenerators.containsKey(id)) {
  3506. throw new IllegalArgumentException(
  3507. "Can not add the same GeneratedColumn twice, id:" + id);
  3508. } else {
  3509. columnGenerators.put(id, generatedColumn);
  3510. /*
  3511. * add to visible column list unless already there (overriding
  3512. * column from DS)
  3513. */
  3514. if (!visibleColumns.contains(id)) {
  3515. visibleColumns.add(id);
  3516. }
  3517. refreshRowCache();
  3518. }
  3519. }
  3520. /**
  3521. * Returns the ColumnGenerator used to generate the given column.
  3522. *
  3523. * @param columnId
  3524. * The id of the generated column
  3525. * @return The ColumnGenerator used for the given columnId or null.
  3526. */
  3527. public ColumnGenerator getColumnGenerator(Object columnId)
  3528. throws IllegalArgumentException {
  3529. return columnGenerators.get(columnId);
  3530. }
  3531. /**
  3532. * Removes a generated column previously added with addGeneratedColumn.
  3533. *
  3534. * @param columnId
  3535. * id of the generated column to remove
  3536. * @return true if the column could be removed (existed in the Table)
  3537. */
  3538. public boolean removeGeneratedColumn(Object columnId) {
  3539. if (columnGenerators.containsKey(columnId)) {
  3540. columnGenerators.remove(columnId);
  3541. // remove column from visibleColumns list unless it exists in
  3542. // container (generator previously overrode this column)
  3543. if (!items.getContainerPropertyIds().contains(columnId)) {
  3544. visibleColumns.remove(columnId);
  3545. }
  3546. refreshRowCache();
  3547. return true;
  3548. } else {
  3549. return false;
  3550. }
  3551. }
  3552. /**
  3553. * Returns item identifiers of the items which are currently rendered on the
  3554. * client.
  3555. * <p>
  3556. * Note, that some due to historical reasons the name of the method is bit
  3557. * misleading. Some items may be partly or totally out of the viewport of
  3558. * the table's scrollable area. Actually detecting rows which can be
  3559. * actually seen by the end user may be problematic due to the client server
  3560. * architecture. Using {@link #getCurrentPageFirstItemId()} combined with
  3561. * {@link #getPageLength()} may produce good enough estimates in some
  3562. * situations.
  3563. *
  3564. * @see com.vaadin.ui.Select#getVisibleItemIds()
  3565. */
  3566. @Override
  3567. public Collection<?> getVisibleItemIds() {
  3568. final LinkedList<Object> visible = new LinkedList<Object>();
  3569. final Object[][] cells = getVisibleCells();
  3570. // may be null if the table has not been rendered yet (e.g. not attached
  3571. // to a layout)
  3572. if (null != cells) {
  3573. for (int i = 0; i < cells[CELL_ITEMID].length; i++) {
  3574. visible.add(cells[CELL_ITEMID][i]);
  3575. }
  3576. }
  3577. return visible;
  3578. }
  3579. /**
  3580. * Container datasource item set change. Table must flush its buffers on
  3581. * change.
  3582. *
  3583. * @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent)
  3584. */
  3585. @Override
  3586. public void containerItemSetChange(Container.ItemSetChangeEvent event) {
  3587. super.containerItemSetChange(event);
  3588. // ensure that page still has first item in page, ignore buffer refresh
  3589. // (forced in this method)
  3590. setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex(), false);
  3591. refreshRowCache();
  3592. }
  3593. /**
  3594. * Container datasource property set change. Table must flush its buffers on
  3595. * change.
  3596. *
  3597. * @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent)
  3598. */
  3599. @Override
  3600. public void containerPropertySetChange(
  3601. Container.PropertySetChangeEvent event) {
  3602. disableContentRefreshing();
  3603. super.containerPropertySetChange(event);
  3604. // sanitetize visibleColumns. note that we are not adding previously
  3605. // non-existing properties as columns
  3606. Collection<?> containerPropertyIds = getContainerDataSource()
  3607. .getContainerPropertyIds();
  3608. LinkedList<Object> newVisibleColumns = new LinkedList<Object>(
  3609. visibleColumns);
  3610. for (Iterator<Object> iterator = newVisibleColumns.iterator(); iterator
  3611. .hasNext();) {
  3612. Object id = iterator.next();
  3613. if (!(containerPropertyIds.contains(id) || columnGenerators
  3614. .containsKey(id))) {
  3615. iterator.remove();
  3616. }
  3617. }
  3618. setVisibleColumns(newVisibleColumns.toArray());
  3619. // same for collapsed columns
  3620. for (Iterator<Object> iterator = collapsedColumns.iterator(); iterator
  3621. .hasNext();) {
  3622. Object id = iterator.next();
  3623. if (!(containerPropertyIds.contains(id) || columnGenerators
  3624. .containsKey(id))) {
  3625. iterator.remove();
  3626. }
  3627. }
  3628. resetPageBuffer();
  3629. enableContentRefreshing(true);
  3630. }
  3631. /**
  3632. * Adding new items is not supported.
  3633. *
  3634. * @throws UnsupportedOperationException
  3635. * if set to true.
  3636. * @see com.vaadin.ui.Select#setNewItemsAllowed(boolean)
  3637. */
  3638. @Override
  3639. public void setNewItemsAllowed(boolean allowNewOptions)
  3640. throws UnsupportedOperationException {
  3641. if (allowNewOptions) {
  3642. throw new UnsupportedOperationException();
  3643. }
  3644. }
  3645. /**
  3646. * Gets the ID of the Item following the Item that corresponds to itemId.
  3647. *
  3648. * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object)
  3649. */
  3650. public Object nextItemId(Object itemId) {
  3651. return ((Container.Ordered) items).nextItemId(itemId);
  3652. }
  3653. /**
  3654. * Gets the ID of the Item preceding the Item that corresponds to the
  3655. * itemId.
  3656. *
  3657. * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object)
  3658. */
  3659. public Object prevItemId(Object itemId) {
  3660. return ((Container.Ordered) items).prevItemId(itemId);
  3661. }
  3662. /**
  3663. * Gets the ID of the first Item in the Container.
  3664. *
  3665. * @see com.vaadin.data.Container.Ordered#firstItemId()
  3666. */
  3667. public Object firstItemId() {
  3668. return ((Container.Ordered) items).firstItemId();
  3669. }
  3670. /**
  3671. * Gets the ID of the last Item in the Container.
  3672. *
  3673. * @see com.vaadin.data.Container.Ordered#lastItemId()
  3674. */
  3675. public Object lastItemId() {
  3676. return ((Container.Ordered) items).lastItemId();
  3677. }
  3678. /**
  3679. * Tests if the Item corresponding to the given Item ID is the first Item in
  3680. * the Container.
  3681. *
  3682. * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object)
  3683. */
  3684. public boolean isFirstId(Object itemId) {
  3685. return ((Container.Ordered) items).isFirstId(itemId);
  3686. }
  3687. /**
  3688. * Tests if the Item corresponding to the given Item ID is the last Item in
  3689. * the Container.
  3690. *
  3691. * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object)
  3692. */
  3693. public boolean isLastId(Object itemId) {
  3694. return ((Container.Ordered) items).isLastId(itemId);
  3695. }
  3696. /**
  3697. * Adds new item after the given item.
  3698. *
  3699. * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
  3700. */
  3701. public Object addItemAfter(Object previousItemId)
  3702. throws UnsupportedOperationException {
  3703. Object itemId = ((Container.Ordered) items)
  3704. .addItemAfter(previousItemId);
  3705. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  3706. refreshRowCache();
  3707. }
  3708. return itemId;
  3709. }
  3710. /**
  3711. * Adds new item after the given item.
  3712. *
  3713. * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
  3714. * java.lang.Object)
  3715. */
  3716. public Item addItemAfter(Object previousItemId, Object newItemId)
  3717. throws UnsupportedOperationException {
  3718. Item item = ((Container.Ordered) items).addItemAfter(previousItemId,
  3719. newItemId);
  3720. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  3721. refreshRowCache();
  3722. }
  3723. return item;
  3724. }
  3725. /**
  3726. * Sets the TableFieldFactory that is used to create editor for table cells.
  3727. *
  3728. * The TableFieldFactory is only used if the Table is editable. By default
  3729. * the DefaultFieldFactory is used.
  3730. *
  3731. * @param fieldFactory
  3732. * the field factory to set.
  3733. * @see #isEditable
  3734. * @see DefaultFieldFactory
  3735. */
  3736. public void setTableFieldFactory(TableFieldFactory fieldFactory) {
  3737. this.fieldFactory = fieldFactory;
  3738. // Assure visual refresh
  3739. refreshRowCache();
  3740. }
  3741. /**
  3742. * Gets the TableFieldFactory that is used to create editor for table cells.
  3743. *
  3744. * The FieldFactory is only used if the Table is editable.
  3745. *
  3746. * @return TableFieldFactory used to create the Field instances.
  3747. * @see #isEditable
  3748. */
  3749. public TableFieldFactory getTableFieldFactory() {
  3750. return fieldFactory;
  3751. }
  3752. /**
  3753. * Is table editable.
  3754. *
  3755. * If table is editable a editor of type Field is created for each table
  3756. * cell. The assigned FieldFactory is used to create the instances.
  3757. *
  3758. * To provide custom editors for table cells create a class implementins the
  3759. * FieldFactory interface, and assign it to table, and set the editable
  3760. * property to true.
  3761. *
  3762. * @return true if table is editable, false oterwise.
  3763. * @see Field
  3764. * @see FieldFactory
  3765. *
  3766. */
  3767. public boolean isEditable() {
  3768. return editable;
  3769. }
  3770. /**
  3771. * Sets the editable property.
  3772. *
  3773. * If table is editable a editor of type Field is created for each table
  3774. * cell. The assigned FieldFactory is used to create the instances.
  3775. *
  3776. * To provide custom editors for table cells create a class implementins the
  3777. * FieldFactory interface, and assign it to table, and set the editable
  3778. * property to true.
  3779. *
  3780. * @param editable
  3781. * true if table should be editable by user.
  3782. * @see Field
  3783. * @see FieldFactory
  3784. *
  3785. */
  3786. public void setEditable(boolean editable) {
  3787. this.editable = editable;
  3788. // Assure visual refresh
  3789. refreshRowCache();
  3790. }
  3791. /**
  3792. * Sorts the table.
  3793. *
  3794. * @throws UnsupportedOperationException
  3795. * if the container data source does not implement
  3796. * Container.Sortable
  3797. * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[],
  3798. * boolean[])
  3799. *
  3800. */
  3801. public void sort(Object[] propertyId, boolean[] ascending)
  3802. throws UnsupportedOperationException {
  3803. final Container c = getContainerDataSource();
  3804. if (c instanceof Container.Sortable) {
  3805. final int pageIndex = getCurrentPageFirstItemIndex();
  3806. ((Container.Sortable) c).sort(propertyId, ascending);
  3807. setCurrentPageFirstItemIndex(pageIndex);
  3808. refreshRowCache();
  3809. } else if (c != null) {
  3810. throw new UnsupportedOperationException(
  3811. "Underlying Data does not allow sorting");
  3812. }
  3813. }
  3814. /**
  3815. * Sorts the table by currently selected sorting column.
  3816. *
  3817. * @throws UnsupportedOperationException
  3818. * if the container data source does not implement
  3819. * Container.Sortable
  3820. */
  3821. public void sort() {
  3822. if (getSortContainerPropertyId() == null) {
  3823. return;
  3824. }
  3825. sort(new Object[] { sortContainerPropertyId },
  3826. new boolean[] { sortAscending });
  3827. }
  3828. /**
  3829. * Gets the container property IDs, which can be used to sort the item.
  3830. *
  3831. * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds()
  3832. */
  3833. public Collection<?> getSortableContainerPropertyIds() {
  3834. final Container c = getContainerDataSource();
  3835. if (c instanceof Container.Sortable && !isSortDisabled()) {
  3836. return ((Container.Sortable) c).getSortableContainerPropertyIds();
  3837. } else {
  3838. return new LinkedList<Object>();
  3839. }
  3840. }
  3841. /**
  3842. * Gets the currently sorted column property ID.
  3843. *
  3844. * @return the Container property id of the currently sorted column.
  3845. */
  3846. public Object getSortContainerPropertyId() {
  3847. return sortContainerPropertyId;
  3848. }
  3849. /**
  3850. * Sets the currently sorted column property id.
  3851. *
  3852. * @param propertyId
  3853. * the Container property id of the currently sorted column.
  3854. */
  3855. public void setSortContainerPropertyId(Object propertyId) {
  3856. setSortContainerPropertyId(propertyId, true);
  3857. }
  3858. /**
  3859. * Internal method to set currently sorted column property id. With doSort
  3860. * flag actual sorting may be bypassed.
  3861. *
  3862. * @param propertyId
  3863. * @param doSort
  3864. */
  3865. private void setSortContainerPropertyId(Object propertyId, boolean doSort) {
  3866. if ((sortContainerPropertyId != null && !sortContainerPropertyId
  3867. .equals(propertyId))
  3868. || (sortContainerPropertyId == null && propertyId != null)) {
  3869. sortContainerPropertyId = propertyId;
  3870. if (doSort) {
  3871. sort();
  3872. // Assures the visual refresh. This should not be necessary as
  3873. // sort() calls refreshRowCache
  3874. refreshRenderedCells();
  3875. }
  3876. }
  3877. }
  3878. /**
  3879. * Is the table currently sorted in ascending order.
  3880. *
  3881. * @return <code>true</code> if ascending, <code>false</code> if descending.
  3882. */
  3883. public boolean isSortAscending() {
  3884. return sortAscending;
  3885. }
  3886. /**
  3887. * Sets the table in ascending order.
  3888. *
  3889. * @param ascending
  3890. * <code>true</code> if ascending, <code>false</code> if
  3891. * descending.
  3892. */
  3893. public void setSortAscending(boolean ascending) {
  3894. setSortAscending(ascending, true);
  3895. }
  3896. /**
  3897. * Internal method to set sort ascending. With doSort flag actual sort can
  3898. * be bypassed.
  3899. *
  3900. * @param ascending
  3901. * @param doSort
  3902. */
  3903. private void setSortAscending(boolean ascending, boolean doSort) {
  3904. if (sortAscending != ascending) {
  3905. sortAscending = ascending;
  3906. if (doSort) {
  3907. sort();
  3908. // Assures the visual refresh. This should not be necessary as
  3909. // sort() calls refreshRowCache
  3910. refreshRenderedCells();
  3911. }
  3912. }
  3913. }
  3914. /**
  3915. * Is sorting disabled altogether.
  3916. *
  3917. * True iff no sortable columns are given even in the case where data source
  3918. * would support this.
  3919. *
  3920. * @return True iff sorting is disabled.
  3921. */
  3922. public boolean isSortDisabled() {
  3923. return sortDisabled;
  3924. }
  3925. /**
  3926. * Disables the sorting altogether.
  3927. *
  3928. * To disable sorting altogether, set to true. In this case no sortable
  3929. * columns are given even in the case where datasource would support this.
  3930. *
  3931. * @param sortDisabled
  3932. * True iff sorting is disabled.
  3933. */
  3934. public void setSortDisabled(boolean sortDisabled) {
  3935. if (this.sortDisabled != sortDisabled) {
  3936. this.sortDisabled = sortDisabled;
  3937. requestRepaint();
  3938. }
  3939. }
  3940. /**
  3941. * Used to create "generated columns"; columns that exist only in the Table,
  3942. * not in the underlying Container. Implement this interface and pass it to
  3943. * Table.addGeneratedColumn along with an id for the column to be generated.
  3944. *
  3945. */
  3946. public interface ColumnGenerator extends Serializable {
  3947. /**
  3948. * Called by Table when a cell in a generated column needs to be
  3949. * generated.
  3950. *
  3951. * @param source
  3952. * the source Table
  3953. * @param itemId
  3954. * the itemId (aka rowId) for the of the cell to be generated
  3955. * @param columnId
  3956. * the id for the generated column (as specified in
  3957. * addGeneratedColumn)
  3958. * @return A {@link Component} that should be rendered in the cell or a
  3959. * {@link String} that should be displayed in the cell. Other
  3960. * return values are not supported.
  3961. */
  3962. public abstract Object generateCell(Table source, Object itemId,
  3963. Object columnId);
  3964. }
  3965. /**
  3966. * Set cell style generator for Table.
  3967. *
  3968. * @param cellStyleGenerator
  3969. * New cell style generator or null to remove generator.
  3970. */
  3971. public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) {
  3972. this.cellStyleGenerator = cellStyleGenerator;
  3973. // Assures the visual refresh. No need to reset the page buffer
  3974. // before as the content has not changed, only the style generators
  3975. refreshRenderedCells();
  3976. }
  3977. /**
  3978. * Get the current cell style generator.
  3979. *
  3980. */
  3981. public CellStyleGenerator getCellStyleGenerator() {
  3982. return cellStyleGenerator;
  3983. }
  3984. /**
  3985. * Allow to define specific style on cells (and rows) contents. Implements
  3986. * this interface and pass it to Table.setCellStyleGenerator. Row styles are
  3987. * generated when porpertyId is null. The CSS class name that will be added
  3988. * to the cell content is <tt>v-table-cell-content-[style name]</tt>, and
  3989. * the row style will be <tt>v-table-row-[style name]</tt>.
  3990. */
  3991. public interface CellStyleGenerator extends Serializable {
  3992. /**
  3993. * Called by Table when a cell (and row) is painted.
  3994. *
  3995. * @param itemId
  3996. * The itemId of the painted cell
  3997. * @param propertyId
  3998. * The propertyId of the cell, null when getting row style
  3999. * @return The style name to add to this cell or row. (the CSS class
  4000. * name will be v-table-cell-content-[style name], or
  4001. * v-table-row-[style name] for rows)
  4002. */
  4003. public abstract String getStyle(Object itemId, Object propertyId);
  4004. }
  4005. public void addListener(ItemClickListener listener) {
  4006. addListener(VScrollTable.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
  4007. listener, ItemClickEvent.ITEM_CLICK_METHOD);
  4008. }
  4009. public void removeListener(ItemClickListener listener) {
  4010. removeListener(VScrollTable.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
  4011. listener);
  4012. }
  4013. // Identical to AbstractCompoenentContainer.setEnabled();
  4014. @Override
  4015. public void setEnabled(boolean enabled) {
  4016. super.setEnabled(enabled);
  4017. if (getParent() != null && !getParent().isEnabled()) {
  4018. // some ancestor still disabled, don't update children
  4019. return;
  4020. } else {
  4021. requestRepaintAll();
  4022. }
  4023. }
  4024. /**
  4025. * Sets the drag start mode of the Table. Drag start mode controls how Table
  4026. * behaves as a drag source.
  4027. *
  4028. * @param newDragMode
  4029. */
  4030. public void setDragMode(TableDragMode newDragMode) {
  4031. dragMode = newDragMode;
  4032. requestRepaint();
  4033. }
  4034. /**
  4035. * @return the current start mode of the Table. Drag start mode controls how
  4036. * Table behaves as a drag source.
  4037. */
  4038. public TableDragMode getDragMode() {
  4039. return dragMode;
  4040. }
  4041. /**
  4042. * Concrete implementation of {@link DataBoundTransferable} for data
  4043. * transferred from a table.
  4044. *
  4045. * @see {@link DataBoundTransferable}.
  4046. *
  4047. * @since 6.3
  4048. */
  4049. public class TableTransferable extends DataBoundTransferable {
  4050. protected TableTransferable(Map<String, Object> rawVariables) {
  4051. super(Table.this, rawVariables);
  4052. Object object = rawVariables.get("itemId");
  4053. if (object != null) {
  4054. setData("itemId", itemIdMapper.get((String) object));
  4055. }
  4056. object = rawVariables.get("propertyId");
  4057. if (object != null) {
  4058. setData("propertyId", columnIdMap.get((String) object));
  4059. }
  4060. }
  4061. @Override
  4062. public Object getItemId() {
  4063. return getData("itemId");
  4064. }
  4065. @Override
  4066. public Object getPropertyId() {
  4067. return getData("propertyId");
  4068. }
  4069. @Override
  4070. public Table getSourceComponent() {
  4071. return (Table) super.getSourceComponent();
  4072. }
  4073. }
  4074. public TableTransferable getTransferable(Map<String, Object> rawVariables) {
  4075. TableTransferable transferable = new TableTransferable(rawVariables);
  4076. return transferable;
  4077. }
  4078. public DropHandler getDropHandler() {
  4079. return dropHandler;
  4080. }
  4081. public void setDropHandler(DropHandler dropHandler) {
  4082. this.dropHandler = dropHandler;
  4083. }
  4084. public AbstractSelectTargetDetails translateDropTargetDetails(
  4085. Map<String, Object> clientVariables) {
  4086. return new AbstractSelectTargetDetails(clientVariables);
  4087. }
  4088. /**
  4089. * Sets the behavior of how the multi-select mode should behave when the
  4090. * table is both selectable and in multi-select mode.
  4091. * <p>
  4092. * Note, that on some clients the mode may not be respected. E.g. on touch
  4093. * based devices CTRL/SHIFT base selection method is invalid, so touch based
  4094. * browsers always use the {@link MultiSelectMode#SIMPLE}.
  4095. *
  4096. * @param mode
  4097. * The select mode of the table
  4098. */
  4099. public void setMultiSelectMode(MultiSelectMode mode) {
  4100. multiSelectMode = mode;
  4101. requestRepaint();
  4102. }
  4103. /**
  4104. * Returns the select mode in which multi-select is used.
  4105. *
  4106. * @return The multi select mode
  4107. */
  4108. public MultiSelectMode getMultiSelectMode() {
  4109. return multiSelectMode;
  4110. }
  4111. /**
  4112. * Lazy loading accept criterion for Table. Accepted target rows are loaded
  4113. * from server once per drag and drop operation. Developer must override one
  4114. * method that decides on which rows the currently dragged data can be
  4115. * dropped.
  4116. *
  4117. * <p>
  4118. * Initially pretty much no data is sent to client. On first required
  4119. * criterion check (per drag request) the client side data structure is
  4120. * initialized from server and no subsequent requests requests are needed
  4121. * during that drag and drop operation.
  4122. */
  4123. public static abstract class TableDropCriterion extends ServerSideCriterion {
  4124. private Table table;
  4125. private Set<Object> allowedItemIds;
  4126. /*
  4127. * (non-Javadoc)
  4128. *
  4129. * @see
  4130. * com.vaadin.event.dd.acceptcriteria.ServerSideCriterion#getIdentifier
  4131. * ()
  4132. */
  4133. @Override
  4134. protected String getIdentifier() {
  4135. return TableDropCriterion.class.getCanonicalName();
  4136. }
  4137. /*
  4138. * (non-Javadoc)
  4139. *
  4140. * @see
  4141. * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#accepts(com.vaadin
  4142. * .event.dd.DragAndDropEvent)
  4143. */
  4144. @SuppressWarnings("unchecked")
  4145. public boolean accept(DragAndDropEvent dragEvent) {
  4146. AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent
  4147. .getTargetDetails();
  4148. table = (Table) dragEvent.getTargetDetails().getTarget();
  4149. Collection<?> visibleItemIds = table.getVisibleItemIds();
  4150. allowedItemIds = getAllowedItemIds(dragEvent, table,
  4151. (Collection<Object>) visibleItemIds);
  4152. return allowedItemIds.contains(dropTargetData.getItemIdOver());
  4153. }
  4154. /*
  4155. * (non-Javadoc)
  4156. *
  4157. * @see
  4158. * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#paintResponse(
  4159. * com.vaadin.terminal.PaintTarget)
  4160. */
  4161. @Override
  4162. public void paintResponse(PaintTarget target) throws PaintException {
  4163. /*
  4164. * send allowed nodes to client so subsequent requests can be
  4165. * avoided
  4166. */
  4167. Object[] array = allowedItemIds.toArray();
  4168. for (int i = 0; i < array.length; i++) {
  4169. String key = table.itemIdMapper.key(array[i]);
  4170. array[i] = key;
  4171. }
  4172. target.addAttribute("allowedIds", array);
  4173. }
  4174. /**
  4175. * @param dragEvent
  4176. * @param table
  4177. * the table for which the allowed item identifiers are
  4178. * defined
  4179. * @param visibleItemIds
  4180. * the list of currently rendered item identifiers, accepted
  4181. * item id's need to be detected only for these visible items
  4182. * @return the set of identifiers for items on which the dragEvent will
  4183. * be accepted
  4184. */
  4185. protected abstract Set<Object> getAllowedItemIds(
  4186. DragAndDropEvent dragEvent, Table table,
  4187. Collection<Object> visibleItemIds);
  4188. }
  4189. /**
  4190. * Click event fired when clicking on the Table headers. The event includes
  4191. * a reference the the Table the event originated from, the property id of
  4192. * the column which header was pressed and details about the mouse event
  4193. * itself.
  4194. */
  4195. public static class HeaderClickEvent extends ClickEvent {
  4196. public static final Method HEADER_CLICK_METHOD;
  4197. static {
  4198. try {
  4199. // Set the header click method
  4200. HEADER_CLICK_METHOD = HeaderClickListener.class
  4201. .getDeclaredMethod("headerClick",
  4202. new Class[] { HeaderClickEvent.class });
  4203. } catch (final java.lang.NoSuchMethodException e) {
  4204. // This should never happen
  4205. throw new java.lang.RuntimeException(e);
  4206. }
  4207. }
  4208. // The property id of the column which header was pressed
  4209. private final Object columnPropertyId;
  4210. public HeaderClickEvent(Component source, Object propertyId,
  4211. MouseEventDetails details) {
  4212. super(source, details);
  4213. columnPropertyId = propertyId;
  4214. }
  4215. /**
  4216. * Gets the property id of the column which header was pressed
  4217. *
  4218. * @return The column propety id
  4219. */
  4220. public Object getPropertyId() {
  4221. return columnPropertyId;
  4222. }
  4223. }
  4224. /**
  4225. * Click event fired when clicking on the Table footers. The event includes
  4226. * a reference the the Table the event originated from, the property id of
  4227. * the column which header was pressed and details about the mouse event
  4228. * itself.
  4229. */
  4230. public static class FooterClickEvent extends ClickEvent {
  4231. public static final Method FOOTER_CLICK_METHOD;
  4232. static {
  4233. try {
  4234. // Set the header click method
  4235. FOOTER_CLICK_METHOD = FooterClickListener.class
  4236. .getDeclaredMethod("footerClick",
  4237. new Class[] { FooterClickEvent.class });
  4238. } catch (final java.lang.NoSuchMethodException e) {
  4239. // This should never happen
  4240. throw new java.lang.RuntimeException(e);
  4241. }
  4242. }
  4243. // The property id of the column which header was pressed
  4244. private final Object columnPropertyId;
  4245. /**
  4246. * Constructor
  4247. *
  4248. * @param source
  4249. * The source of the component
  4250. * @param propertyId
  4251. * The propertyId of the column
  4252. * @param details
  4253. * The mouse details of the click
  4254. */
  4255. public FooterClickEvent(Component source, Object propertyId,
  4256. MouseEventDetails details) {
  4257. super(source, details);
  4258. columnPropertyId = propertyId;
  4259. }
  4260. /**
  4261. * Gets the property id of the column which header was pressed
  4262. *
  4263. * @return The column propety id
  4264. */
  4265. public Object getPropertyId() {
  4266. return columnPropertyId;
  4267. }
  4268. }
  4269. /**
  4270. * Interface for the listener for column header mouse click events. The
  4271. * headerClick method is called when the user presses a header column cell.
  4272. */
  4273. public interface HeaderClickListener extends Serializable {
  4274. /**
  4275. * Called when a user clicks a header column cell
  4276. *
  4277. * @param event
  4278. * The event which contains information about the column and
  4279. * the mouse click event
  4280. */
  4281. public void headerClick(HeaderClickEvent event);
  4282. }
  4283. /**
  4284. * Interface for the listener for column footer mouse click events. The
  4285. * footerClick method is called when the user presses a footer column cell.
  4286. */
  4287. public interface FooterClickListener extends Serializable {
  4288. /**
  4289. * Called when a user clicks a footer column cell
  4290. *
  4291. * @param event
  4292. * The event which contains information about the column and
  4293. * the mouse click event
  4294. */
  4295. public void footerClick(FooterClickEvent event);
  4296. }
  4297. /**
  4298. * Adds a header click listener which handles the click events when the user
  4299. * clicks on a column header cell in the Table.
  4300. * <p>
  4301. * The listener will receive events which contain information about which
  4302. * column was clicked and some details about the mouse event.
  4303. * </p>
  4304. *
  4305. * @param listener
  4306. * The handler which should handle the header click events.
  4307. */
  4308. public void addListener(HeaderClickListener listener) {
  4309. addListener(VScrollTable.HEADER_CLICK_EVENT_ID, HeaderClickEvent.class,
  4310. listener, HeaderClickEvent.HEADER_CLICK_METHOD);
  4311. }
  4312. /**
  4313. * Removes a header click listener
  4314. *
  4315. * @param listener
  4316. * The listener to remove.
  4317. */
  4318. public void removeListener(HeaderClickListener listener) {
  4319. removeListener(VScrollTable.HEADER_CLICK_EVENT_ID,
  4320. HeaderClickEvent.class, listener);
  4321. }
  4322. /**
  4323. * Adds a footer click listener which handles the click events when the user
  4324. * clicks on a column footer cell in the Table.
  4325. * <p>
  4326. * The listener will receive events which contain information about which
  4327. * column was clicked and some details about the mouse event.
  4328. * </p>
  4329. *
  4330. * @param listener
  4331. * The handler which should handle the footer click events.
  4332. */
  4333. public void addListener(FooterClickListener listener) {
  4334. addListener(VScrollTable.FOOTER_CLICK_EVENT_ID, FooterClickEvent.class,
  4335. listener, FooterClickEvent.FOOTER_CLICK_METHOD);
  4336. }
  4337. /**
  4338. * Removes a footer click listener
  4339. *
  4340. * @param listener
  4341. * The listener to remove.
  4342. */
  4343. public void removeListener(FooterClickListener listener) {
  4344. removeListener(VScrollTable.FOOTER_CLICK_EVENT_ID,
  4345. FooterClickEvent.class, listener);
  4346. }
  4347. /**
  4348. * Gets the footer caption beneath the rows
  4349. *
  4350. * @param propertyId
  4351. * The propertyId of the column *
  4352. * @return The caption of the footer or NULL if not set
  4353. */
  4354. public String getColumnFooter(Object propertyId) {
  4355. return columnFooters.get(propertyId);
  4356. }
  4357. /**
  4358. * Sets the column footer caption. The column footer caption is the text
  4359. * displayed beneath the column if footers have been set visible.
  4360. *
  4361. * @param propertyId
  4362. * The properyId of the column
  4363. *
  4364. * @param footer
  4365. * The caption of the footer
  4366. */
  4367. public void setColumnFooter(Object propertyId, String footer) {
  4368. if (footer == null) {
  4369. columnFooters.remove(propertyId);
  4370. } else {
  4371. columnFooters.put(propertyId, footer);
  4372. }
  4373. requestRepaint();
  4374. }
  4375. /**
  4376. * Sets the footer visible in the bottom of the table.
  4377. * <p>
  4378. * The footer can be used to add column related data like sums to the bottom
  4379. * of the Table using setColumnFooter(Object propertyId, String footer).
  4380. * </p>
  4381. *
  4382. * @param visible
  4383. * Should the footer be visible
  4384. */
  4385. public void setFooterVisible(boolean visible) {
  4386. if (visible != columnFootersVisible) {
  4387. columnFootersVisible = visible;
  4388. requestRepaint();
  4389. }
  4390. }
  4391. /**
  4392. * Is the footer currently visible?
  4393. *
  4394. * @return Returns true if visible else false
  4395. */
  4396. public boolean isFooterVisible() {
  4397. return columnFootersVisible;
  4398. }
  4399. /**
  4400. * This event is fired when a column is resized. The event contains the
  4401. * columns property id which was fired, the previous width of the column and
  4402. * the width of the column after the resize.
  4403. */
  4404. public static class ColumnResizeEvent extends Component.Event {
  4405. public static final Method COLUMN_RESIZE_METHOD;
  4406. static {
  4407. try {
  4408. COLUMN_RESIZE_METHOD = ColumnResizeListener.class
  4409. .getDeclaredMethod("columnResize",
  4410. new Class[] { ColumnResizeEvent.class });
  4411. } catch (final java.lang.NoSuchMethodException e) {
  4412. // This should never happen
  4413. throw new java.lang.RuntimeException(e);
  4414. }
  4415. }
  4416. private final int previousWidth;
  4417. private final int currentWidth;
  4418. private final Object columnPropertyId;
  4419. /**
  4420. * Constructor
  4421. *
  4422. * @param source
  4423. * The source of the event
  4424. * @param propertyId
  4425. * The columns property id
  4426. * @param previous
  4427. * The width in pixels of the column before the resize event
  4428. * @param current
  4429. * The width in pixels of the column after the resize event
  4430. */
  4431. public ColumnResizeEvent(Component source, Object propertyId,
  4432. int previous, int current) {
  4433. super(source);
  4434. previousWidth = previous;
  4435. currentWidth = current;
  4436. columnPropertyId = propertyId;
  4437. }
  4438. /**
  4439. * Get the column property id of the column that was resized.
  4440. *
  4441. * @return The column property id
  4442. */
  4443. public Object getPropertyId() {
  4444. return columnPropertyId;
  4445. }
  4446. /**
  4447. * Get the width in pixels of the column before the resize event
  4448. *
  4449. * @return Width in pixels
  4450. */
  4451. public int getPreviousWidth() {
  4452. return previousWidth;
  4453. }
  4454. /**
  4455. * Get the width in pixels of the column after the resize event
  4456. *
  4457. * @return Width in pixels
  4458. */
  4459. public int getCurrentWidth() {
  4460. return currentWidth;
  4461. }
  4462. }
  4463. /**
  4464. * Interface for listening to column resize events.
  4465. */
  4466. public interface ColumnResizeListener extends Serializable {
  4467. /**
  4468. * This method is triggered when the column has been resized
  4469. *
  4470. * @param event
  4471. * The event which contains the column property id, the
  4472. * previous width of the column and the current width of the
  4473. * column
  4474. */
  4475. public void columnResize(ColumnResizeEvent event);
  4476. }
  4477. /**
  4478. * Adds a column resize listener to the Table. A column resize listener is
  4479. * called when a user resizes a columns width.
  4480. *
  4481. * @param listener
  4482. * The listener to attach to the Table
  4483. */
  4484. public void addListener(ColumnResizeListener listener) {
  4485. addListener(VScrollTable.COLUMN_RESIZE_EVENT_ID,
  4486. ColumnResizeEvent.class, listener,
  4487. ColumnResizeEvent.COLUMN_RESIZE_METHOD);
  4488. }
  4489. /**
  4490. * Removes a column resize listener from the Table.
  4491. *
  4492. * @param listener
  4493. * The listener to remove
  4494. */
  4495. public void removeListener(ColumnResizeListener listener) {
  4496. removeListener(VScrollTable.COLUMN_RESIZE_EVENT_ID,
  4497. ColumnResizeEvent.class, listener);
  4498. }
  4499. /**
  4500. * This event is fired when a columns are reordered by the end user user.
  4501. */
  4502. public static class ColumnReorderEvent extends Component.Event {
  4503. public static final Method METHOD;
  4504. static {
  4505. try {
  4506. METHOD = ColumnReorderListener.class.getDeclaredMethod(
  4507. "columnReorder",
  4508. new Class[] { ColumnReorderEvent.class });
  4509. } catch (final java.lang.NoSuchMethodException e) {
  4510. // This should never happen
  4511. throw new java.lang.RuntimeException(e);
  4512. }
  4513. }
  4514. /**
  4515. * Constructor
  4516. *
  4517. * @param source
  4518. * The source of the event
  4519. */
  4520. public ColumnReorderEvent(Component source) {
  4521. super(source);
  4522. }
  4523. }
  4524. /**
  4525. * Interface for listening to column reorder events.
  4526. */
  4527. public interface ColumnReorderListener extends Serializable {
  4528. /**
  4529. * This method is triggered when the column has been reordered
  4530. *
  4531. * @param event
  4532. */
  4533. public void columnReorder(ColumnReorderEvent event);
  4534. }
  4535. /**
  4536. * Adds a column reorder listener to the Table. A column reorder listener is
  4537. * called when a user reorders columns.
  4538. *
  4539. * @param listener
  4540. * The listener to attach to the Table
  4541. */
  4542. public void addListener(ColumnReorderListener listener) {
  4543. addListener(VScrollTable.COLUMN_REORDER_EVENT_ID,
  4544. ColumnReorderEvent.class, listener, ColumnReorderEvent.METHOD);
  4545. }
  4546. /**
  4547. * Removes a column reorder listener from the Table.
  4548. *
  4549. * @param listener
  4550. * The listener to remove
  4551. */
  4552. public void removeListener(ColumnReorderListener listener) {
  4553. removeListener(VScrollTable.COLUMN_REORDER_EVENT_ID,
  4554. ColumnReorderEvent.class, listener);
  4555. }
  4556. /**
  4557. * Set the item description generator which generates tooltips for cells and
  4558. * rows in the Table
  4559. *
  4560. * @param generator
  4561. * The generator to use or null to disable
  4562. */
  4563. public void setItemDescriptionGenerator(ItemDescriptionGenerator generator) {
  4564. if (generator != itemDescriptionGenerator) {
  4565. itemDescriptionGenerator = generator;
  4566. // Assures the visual refresh. No need to reset the page buffer
  4567. // before as the content has not changed, only the descriptions
  4568. refreshRenderedCells();
  4569. }
  4570. }
  4571. /**
  4572. * Get the item description generator which generates tooltips for cells and
  4573. * rows in the Table.
  4574. */
  4575. public ItemDescriptionGenerator getItemDescriptionGenerator() {
  4576. return itemDescriptionGenerator;
  4577. }
  4578. /**
  4579. * Row generators can be used to replace certain items in a table with a
  4580. * generated string. The generator is called each time the table is
  4581. * rendered, which means that new strings can be generated each time.
  4582. *
  4583. * Row generators can be used for e.g. summary rows or grouping of items.
  4584. */
  4585. public interface RowGenerator extends Serializable {
  4586. /**
  4587. * Called for every row that is painted in the Table. Returning a
  4588. * GeneratedRow object will cause the row to be painted based on the
  4589. * contents of the GeneratedRow. A generated row is by default styled
  4590. * similarly to a header or footer row.
  4591. * <p>
  4592. * The GeneratedRow data object contains the text that should be
  4593. * rendered in the row. The itemId in the container thus works only as a
  4594. * placeholder.
  4595. * <p>
  4596. * If GeneratedRow.setSpanColumns(true) is used, there will be one
  4597. * String spanning all columns (use setText("Spanning text")). Otherwise
  4598. * you can define one String per visible column.
  4599. * <p>
  4600. * If GeneratedRow.setRenderAsHtml(true) is used, the strings can
  4601. * contain HTML markup, otherwise all strings will be rendered as text
  4602. * (the default).
  4603. * <p>
  4604. * A "v-table-generated-row" CSS class is added to all generated rows.
  4605. * For custom styling of a generated row you can combine a RowGenerator
  4606. * with a CellStyleGenerator.
  4607. * <p>
  4608. *
  4609. * @param table
  4610. * The Table that is being painted
  4611. * @param itemId
  4612. * The itemId for the row
  4613. * @return A GeneratedRow describing how the row should be painted or
  4614. * null to paint the row with the contents from the container
  4615. */
  4616. public GeneratedRow generateRow(Table table, Object itemId);
  4617. }
  4618. public static class GeneratedRow implements Serializable {
  4619. private boolean htmlContentAllowed = false;
  4620. private boolean spanColumns = false;
  4621. private String[] text = null;
  4622. /**
  4623. * Creates a new generated row. If only one string is passed in, columns
  4624. * are automatically spanned.
  4625. *
  4626. * @param text
  4627. */
  4628. public GeneratedRow(String... text) {
  4629. setHtmlContentAllowed(false);
  4630. setSpanColumns(text == null || text.length == 1);
  4631. setText(text);
  4632. }
  4633. /**
  4634. * Pass one String if spanColumns is used, one String for each visible
  4635. * column otherwise
  4636. */
  4637. public void setText(String... text) {
  4638. if (text == null || (text.length == 1 && text[0] == null)) {
  4639. text = new String[] { "" };
  4640. }
  4641. this.text = text;
  4642. }
  4643. protected String[] getText() {
  4644. return text;
  4645. }
  4646. protected Object getValue() {
  4647. return getText();
  4648. }
  4649. protected boolean isHtmlContentAllowed() {
  4650. return htmlContentAllowed;
  4651. }
  4652. /**
  4653. * If set to true, all strings passed to {@link #setText(String...)}
  4654. * will be rendered as HTML.
  4655. *
  4656. * @param htmlContentAllowed
  4657. */
  4658. public void setHtmlContentAllowed(boolean htmlContentAllowed) {
  4659. this.htmlContentAllowed = htmlContentAllowed;
  4660. }
  4661. protected boolean isSpanColumns() {
  4662. return spanColumns;
  4663. }
  4664. /**
  4665. * If set to true, only one string will be rendered, spanning the entire
  4666. * row.
  4667. *
  4668. * @param spanColumns
  4669. */
  4670. public void setSpanColumns(boolean spanColumns) {
  4671. this.spanColumns = spanColumns;
  4672. }
  4673. }
  4674. /**
  4675. * Assigns a row generator to the table. The row generator will be able to
  4676. * replace rows in the table when it is rendered.
  4677. *
  4678. * @param generator
  4679. * the new row generator
  4680. */
  4681. public void setRowGenerator(RowGenerator generator) {
  4682. rowGenerator = generator;
  4683. refreshRowCache();
  4684. }
  4685. /**
  4686. * @return the current row generator
  4687. */
  4688. public RowGenerator getRowGenerator() {
  4689. return rowGenerator;
  4690. }
  4691. /**
  4692. * Sets a converter for a property id.
  4693. * <p>
  4694. * The converter is used to format the the data for the given property id
  4695. * before displaying it in the table.
  4696. * </p>
  4697. *
  4698. * @param propertyId
  4699. * The propertyId to format using the converter
  4700. * @param converter
  4701. * The converter to use for the property id
  4702. */
  4703. public void setConverter(Object propertyId, Converter<String, ?> converter) {
  4704. if (!getContainerPropertyIds().contains(propertyId)) {
  4705. throw new IllegalArgumentException("PropertyId " + propertyId
  4706. + " must be in the container");
  4707. }
  4708. // FIXME: This check should be here but primitive types like Boolean
  4709. // formatter for boolean property must be handled
  4710. // if (!converter.getSourceType().isAssignableFrom(getType(propertyId)))
  4711. // {
  4712. // throw new IllegalArgumentException("Property type ("
  4713. // + getType(propertyId)
  4714. // + ") must match converter source type ("
  4715. // + converter.getSourceType() + ")");
  4716. // }
  4717. propertyValueConverters.put(propertyId,
  4718. (Converter<String, Object>) converter);
  4719. refreshRowCache();
  4720. }
  4721. /**
  4722. * Checks if there is a converter set explicitly for the given property id.
  4723. *
  4724. * @param propertyId
  4725. * The propertyId to check
  4726. * @return true if a converter has been set for the property id, false
  4727. * otherwise
  4728. */
  4729. protected boolean hasConverter(Object propertyId) {
  4730. return propertyValueConverters.containsKey(propertyId);
  4731. }
  4732. /**
  4733. * Returns the converter used to format the given propertyId.
  4734. *
  4735. * @param propertyId
  4736. * The propertyId to check
  4737. * @return The converter used to format the propertyId or null if no
  4738. * converter has been set
  4739. */
  4740. public Converter<String, Object> getConverter(Object propertyId) {
  4741. return propertyValueConverters.get(propertyId);
  4742. }
  4743. @Override
  4744. public void setVisible(boolean visible) {
  4745. if (visible) {
  4746. // We need to ensure that the rows are sent to the client when the
  4747. // Table is made visible if it has been rendered as invisible.
  4748. setRowCacheInvalidated(true);
  4749. }
  4750. super.setVisible(visible);
  4751. }
  4752. public Iterator<Component> iterator() {
  4753. return getComponentIterator();
  4754. }
  4755. public Iterator<Component> getComponentIterator() {
  4756. if (visibleComponents == null) {
  4757. Collection<Component> empty = Collections.emptyList();
  4758. return empty.iterator();
  4759. }
  4760. return visibleComponents.iterator();
  4761. }
  4762. public boolean isComponentVisible(Component childComponent) {
  4763. return true;
  4764. }
  4765. private final Logger getLogger() {
  4766. if (logger == null) {
  4767. logger = Logger.getLogger(Table.class.getName());
  4768. }
  4769. return logger;
  4770. }
  4771. }