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

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