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 182KB

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