123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686 |
- /*
- * Copyright 2000-2018 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
- package com.vaadin.ui;
-
- import java.io.Serializable;
- import java.lang.reflect.Method;
- import java.lang.reflect.Type;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.EnumSet;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.LinkedHashMap;
- import java.util.LinkedHashSet;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Locale;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import java.util.logging.Level;
- import java.util.logging.Logger;
-
- import org.jsoup.Jsoup;
- import org.jsoup.nodes.Attributes;
- import org.jsoup.nodes.Element;
- import org.jsoup.select.Elements;
-
- import com.vaadin.data.Container;
- import com.vaadin.data.Container.Indexed;
- import com.vaadin.data.Container.ItemSetChangeEvent;
- import com.vaadin.data.Container.ItemSetChangeListener;
- import com.vaadin.data.Container.ItemSetChangeNotifier;
- import com.vaadin.data.Container.PropertySetChangeEvent;
- import com.vaadin.data.Container.PropertySetChangeListener;
- import com.vaadin.data.Container.PropertySetChangeNotifier;
- import com.vaadin.data.Container.Sortable;
- import com.vaadin.data.Item;
- import com.vaadin.data.Property;
- import com.vaadin.data.Validator.InvalidValueException;
- import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory;
- import com.vaadin.data.fieldgroup.FieldGroup;
- import com.vaadin.data.fieldgroup.FieldGroup.CommitException;
- import com.vaadin.data.fieldgroup.FieldGroupFieldFactory;
- import com.vaadin.data.sort.Sort;
- import com.vaadin.data.sort.SortOrder;
- import com.vaadin.data.util.IndexedContainer;
- import com.vaadin.data.util.converter.Converter;
- import com.vaadin.data.util.converter.ConverterUtil;
- import com.vaadin.event.ContextClickEvent;
- import com.vaadin.event.ItemClickEvent;
- import com.vaadin.event.ItemClickEvent.ItemClickListener;
- import com.vaadin.event.ItemClickEvent.ItemClickNotifier;
- import com.vaadin.event.SelectionEvent;
- import com.vaadin.event.SelectionEvent.SelectionListener;
- import com.vaadin.event.SelectionEvent.SelectionNotifier;
- import com.vaadin.event.SortEvent;
- import com.vaadin.event.SortEvent.SortListener;
- import com.vaadin.event.SortEvent.SortNotifier;
- import com.vaadin.server.AbstractClientConnector;
- import com.vaadin.server.AbstractExtension;
- import com.vaadin.server.EncodeResult;
- import com.vaadin.server.ErrorMessage;
- import com.vaadin.server.Extension;
- import com.vaadin.server.JsonCodec;
- import com.vaadin.server.KeyMapper;
- import com.vaadin.server.VaadinSession;
- import com.vaadin.server.communication.data.DataGenerator;
- import com.vaadin.server.communication.data.RpcDataProviderExtension;
- import com.vaadin.shared.MouseEventDetails;
- import com.vaadin.shared.data.sort.SortDirection;
- import com.vaadin.shared.ui.grid.ColumnResizeMode;
- import com.vaadin.shared.ui.grid.EditorClientRpc;
- import com.vaadin.shared.ui.grid.EditorServerRpc;
- import com.vaadin.shared.ui.grid.GridClientRpc;
- import com.vaadin.shared.ui.grid.GridColumnState;
- import com.vaadin.shared.ui.grid.GridConstants;
- import com.vaadin.shared.ui.grid.GridConstants.Section;
- import com.vaadin.shared.ui.grid.GridServerRpc;
- import com.vaadin.shared.ui.grid.GridState;
- import com.vaadin.shared.ui.grid.GridStaticCellType;
- import com.vaadin.shared.ui.grid.GridStaticSectionState;
- import com.vaadin.shared.ui.grid.GridStaticSectionState.CellState;
- import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState;
- import com.vaadin.shared.ui.grid.HeightMode;
- import com.vaadin.shared.ui.grid.ScrollDestination;
- import com.vaadin.shared.ui.grid.selection.MultiSelectionModelServerRpc;
- import com.vaadin.shared.ui.grid.selection.MultiSelectionModelState;
- import com.vaadin.shared.ui.grid.selection.SingleSelectionModelServerRpc;
- import com.vaadin.shared.ui.grid.selection.SingleSelectionModelState;
- import com.vaadin.shared.ui.label.ContentMode;
- import com.vaadin.shared.util.SharedUtil;
- import com.vaadin.ui.Grid.SelectionModel.HasUserSelectionAllowed;
- import com.vaadin.ui.declarative.DesignAttributeHandler;
- import com.vaadin.ui.declarative.DesignContext;
- import com.vaadin.ui.declarative.DesignException;
- import com.vaadin.ui.declarative.DesignFormatter;
- import com.vaadin.ui.renderers.HtmlRenderer;
- import com.vaadin.ui.renderers.Renderer;
- import com.vaadin.ui.renderers.TextRenderer;
- import com.vaadin.util.ReflectTools;
-
- import elemental.json.Json;
- import elemental.json.JsonObject;
- import elemental.json.JsonValue;
-
- /**
- * A grid component for displaying tabular data.
- * <p>
- * Grid is always bound to a {@link Container.Indexed}, but is not a
- * {@code Container} of any kind in of itself. The contents of the given
- * Container is displayed with the help of {@link Renderer Renderers}.
- *
- * <h3 id="grid-headers-and-footers">Headers and Footers</h3>
- * <p>
- *
- *
- * <h3 id="grid-converters-and-renderers">Converters and Renderers</h3>
- * <p>
- * Each column has its own {@link Renderer} that displays data into something
- * that can be displayed in the browser. That data is first converted with a
- * {@link Converter} into something that the Renderer can process. This can also
- * be an implicit step - if a column has a simple data type, like a String, no
- * explicit assignment is needed.
- * <p>
- * Usually a renderer takes some kind of object, and converts it into a
- * HTML-formatted string.
- * <p>
- * <code><pre>
- * Grid grid = new Grid(myContainer);
- * Column column = grid.getColumn(STRING_DATE_PROPERTY);
- * column.setConverter(new StringToDateConverter());
- * column.setRenderer(new MyColorfulDateRenderer());
- * </pre></code>
- *
- * <h3 id="grid-lazyloading">Lazy Loading</h3>
- * <p>
- * The data is accessed as it is needed by Grid and not any sooner. In other
- * words, if the given Container is huge, but only the first few rows are
- * displayed to the user, only those (and a few more, for caching purposes) are
- * accessed.
- *
- * <h3 id="grid-selection-modes-and-models">Selection Modes and Models</h3>
- * <p>
- * Grid supports three selection <em>{@link SelectionMode modes}</em> (single,
- * multi, none), and comes bundled with one <em>{@link SelectionModel
- * model}</em> for each of the modes. The distinction between a selection mode
- * and selection model is as follows: a <em>mode</em> essentially says whether
- * you can have one, many or no rows selected. The model, however, has the
- * behavioral details of each. A single selection model may require that the
- * user deselects one row before selecting another one. A variant of a
- * multiselect might have a configurable maximum of rows that may be selected.
- * And so on.
- * <p>
- * <code><pre>
- * Grid grid = new Grid(myContainer);
- *
- * // uses the bundled SingleSelectionModel class
- * grid.setSelectionMode(SelectionMode.SINGLE);
- *
- * // changes the behavior to a custom selection model
- * grid.setSelectionModel(new MyTwoSelectionModel());
- * </pre></code>
- *
- * @since 7.4
- * @author Vaadin Ltd
- */
- public class Grid extends AbstractFocusable implements SelectionNotifier,
- SortNotifier, SelectiveRenderer, ItemClickNotifier {
-
- /**
- * An event listener for column visibility change events in the Grid.
- *
- * @since 7.5.0
- */
- public interface ColumnVisibilityChangeListener extends Serializable {
- /**
- * Called when a column has become hidden or unhidden.
- *
- * @param event
- */
- void columnVisibilityChanged(ColumnVisibilityChangeEvent event);
- }
-
- /**
- * An event that is fired when a column's visibility changes.
- *
- * @since 7.5.0
- */
- public static class ColumnVisibilityChangeEvent extends Component.Event {
-
- private final Column column;
- private final boolean userOriginated;
- private final boolean hidden;
-
- /**
- * Constructor for a column visibility change event.
- *
- * @param source
- * the grid from which this event originates
- * @param column
- * the column that changed its visibility
- * @param hidden
- * <code>true</code> if the column was hidden,
- * <code>false</code> if it became visible
- * @param isUserOriginated
- * <code>true</code> iff the event was triggered by an UI
- * interaction
- */
- public ColumnVisibilityChangeEvent(Grid source, Column column,
- boolean hidden, boolean isUserOriginated) {
- super(source);
- this.column = column;
- this.hidden = hidden;
- userOriginated = isUserOriginated;
- }
-
- /**
- * Gets the column that became hidden or visible.
- *
- * @return the column that became hidden or visible.
- * @see Column#isHidden()
- */
- public Column getColumn() {
- return column;
- }
-
- /**
- * Was the column set hidden or visible.
- *
- * @return <code>true</code> if the column was hidden <code>false</code>
- * if it was set visible
- */
- public boolean isHidden() {
- return hidden;
- }
-
- /**
- * Returns <code>true</code> if the column reorder was done by the user,
- * <code>false</code> if not and it was triggered by server side code.
- *
- * @return <code>true</code> if event is a result of user interaction
- */
- public boolean isUserOriginated() {
- return userOriginated;
- }
- }
-
- /**
- * A callback interface for generating details for a particular row in Grid.
- *
- * @since 7.5.0
- * @author Vaadin Ltd
- * @see DetailsGenerator#NULL
- */
- public interface DetailsGenerator extends Serializable {
-
- /** A details generator that provides no details */
- public DetailsGenerator NULL = new DetailsGenerator() {
- @Override
- public Component getDetails(RowReference rowReference) {
- return null;
- }
- };
-
- /**
- * This method is called for whenever a details row needs to be shown on
- * the client. Grid removes all of its references to details components
- * when they are no longer displayed on the client-side and will
- * re-request once needed again.
- * <p>
- * <em>Note:</em> If a component gets generated, it may not be manually
- * attached anywhere. The same details component can not be displayed
- * for multiple different rows.
- *
- * @param rowReference
- * the reference for the row for which to generate details
- * @return the details for the given row, or <code>null</code> to leave
- * the details empty.
- */
- Component getDetails(RowReference rowReference);
- }
-
- /**
- * A class that manages details components by calling
- * {@link DetailsGenerator} as needed. Details components are attached by
- * this class when the {@link RpcDataProviderExtension} is sending data to
- * the client. Details components are detached and forgotten when client
- * informs that it has dropped the corresponding item.
- *
- * @since 7.6.1
- */
- public final static class DetailComponentManager
- extends AbstractGridExtension implements DataGenerator {
-
- /**
- * The user-defined details generator.
- *
- * @see #setDetailsGenerator(DetailsGenerator)
- */
- private DetailsGenerator detailsGenerator;
-
- /**
- * This map represents all details that are currently visible on the
- * client. Details components get destroyed once they scroll out of
- * view.
- */
- private final Map<Object, Component> itemIdToDetailsComponent = new HashMap<Object, Component>();
-
- /**
- * Set of item ids that got <code>null</code> from DetailsGenerator when
- * {@link DetailsGenerator#getDetails(RowReference)} was called.
- */
- private final Set<Object> emptyDetails = new HashSet<Object>();
-
- /**
- * Set of item IDs for all open details rows. Contains even the ones
- * that are not currently visible on the client.
- */
- private final Set<Object> openDetails = new HashSet<Object>();
-
- public DetailComponentManager(Grid grid) {
- this(grid, DetailsGenerator.NULL);
- }
-
- public DetailComponentManager(Grid grid,
- DetailsGenerator detailsGenerator) {
- super(grid);
- setDetailsGenerator(detailsGenerator);
- }
-
- /**
- * Creates a details component with the help of the user-defined
- * {@link DetailsGenerator}.
- * <p>
- * This method attaches created components to the parent {@link Grid}.
- *
- * @param itemId
- * the item id for which to create the details component.
- * @throws IllegalStateException
- * if the current details generator provides a component
- * that was manually attached.
- */
- private void createDetails(Object itemId) throws IllegalStateException {
- assert itemId != null : "itemId was null";
-
- if (itemIdToDetailsComponent.containsKey(itemId)
- || emptyDetails.contains(itemId)) {
- // Don't overwrite existing components
- return;
- }
-
- RowReference rowReference = new RowReference(getParentGrid());
- rowReference.set(itemId);
-
- DetailsGenerator detailsGenerator = getParentGrid()
- .getDetailsGenerator();
- Component details = detailsGenerator.getDetails(rowReference);
- if (details != null) {
- if (details.getParent() != null) {
- String name = detailsGenerator.getClass().getName();
- throw new IllegalStateException(
- name + " generated a details component that already "
- + "was attached. (itemId: " + itemId
- + ", component: " + details + ")");
- }
-
- itemIdToDetailsComponent.put(itemId, details);
-
- addComponentToGrid(details);
-
- assert !emptyDetails.contains(itemId) : "Bookeeping thinks "
- + "itemId is empty even though we just created a "
- + "component for it (" + itemId + ")";
- } else {
- emptyDetails.add(itemId);
- }
-
- }
-
- /**
- * Destroys a details component correctly.
- * <p>
- * This method will detach the component from parent {@link Grid}.
- *
- * @param itemId
- * the item id for which to destroy the details component
- */
- private void destroyDetails(Object itemId) {
- emptyDetails.remove(itemId);
-
- Component removedComponent = itemIdToDetailsComponent
- .remove(itemId);
- if (removedComponent == null) {
- return;
- }
-
- removeComponentFromGrid(removedComponent);
- }
-
- /**
- * Recreates all visible details components.
- */
- public void refreshDetails() {
- Set<Object> visibleItemIds = new HashSet<Object>(
- itemIdToDetailsComponent.keySet());
- for (Object itemId : visibleItemIds) {
- destroyDetails(itemId);
- createDetails(itemId);
- refreshRow(itemId);
- }
- }
-
- /**
- * Sets details visiblity status of given item id.
- *
- * @param itemId
- * item id to set
- * @param visible
- * <code>true</code> if visible; <code>false</code> if not
- */
- public void setDetailsVisible(Object itemId, boolean visible) {
- if ((visible && openDetails.contains(itemId))
- || (!visible && !openDetails.contains(itemId))) {
- return;
- }
-
- if (visible) {
- openDetails.add(itemId);
- refreshRow(itemId);
- } else {
- openDetails.remove(itemId);
- destroyDetails(itemId);
- refreshRow(itemId);
- }
- }
-
- @Override
- public void generateData(Object itemId, Item item, JsonObject rowData) {
- // DetailComponentManager should not send anything if details
- // generator is the default null version.
- if (openDetails.contains(itemId)
- && !detailsGenerator.equals(DetailsGenerator.NULL)) {
- // Double check to be sure details component exists.
- createDetails(itemId);
-
- Component detailsComponent = itemIdToDetailsComponent
- .get(itemId);
- rowData.put(GridState.JSONKEY_DETAILS_VISIBLE,
- (detailsComponent != null
- ? detailsComponent.getConnectorId() : ""));
- }
- }
-
- @Override
- public void destroyData(Object itemId) {
- if (openDetails.contains(itemId)) {
- destroyDetails(itemId);
- }
- }
-
- /**
- * Sets a new details generator for row details.
- * <p>
- * The currently opened row details will be re-rendered.
- *
- * @param detailsGenerator
- * the details generator to set
- * @throws IllegalArgumentException
- * if detailsGenerator is <code>null</code>;
- */
- public void setDetailsGenerator(DetailsGenerator detailsGenerator)
- throws IllegalArgumentException {
- if (detailsGenerator == null) {
- throw new IllegalArgumentException(
- "Details generator may not be null");
- } else if (detailsGenerator == this.detailsGenerator) {
- return;
- }
-
- this.detailsGenerator = detailsGenerator;
-
- refreshDetails();
- }
-
- /**
- * Gets the current details generator for row details.
- *
- * @return the detailsGenerator the current details generator
- */
- public DetailsGenerator getDetailsGenerator() {
- return detailsGenerator;
- }
-
- /**
- * Checks whether details are visible for the given item.
- *
- * @param itemId
- * the id of the item for which to check details visibility
- * @return <code>true</code> iff the details are visible
- */
- public boolean isDetailsVisible(Object itemId) {
- return openDetails.contains(itemId);
- }
- }
-
- /**
- * Custom field group that allows finding property types before an item has
- * been bound.
- */
- private final class CustomFieldGroup extends FieldGroup {
-
- public CustomFieldGroup() {
- setFieldFactory(EditorFieldFactory.get());
- }
-
- @Override
- protected Class<?> getPropertyType(Object propertyId)
- throws BindException {
- if (getItemDataSource() == null) {
- return datasource.getType(propertyId);
- } else {
- return super.getPropertyType(propertyId);
- }
- }
-
- @Override
- protected <T extends Field> T build(String caption, Class<?> dataType,
- Class<T> fieldType) throws BindException {
- T field = super.build(caption, dataType, fieldType);
- if (field instanceof CheckBox) {
- field.setCaption(null);
- }
- return field;
- }
-
- @Override
- protected void bindFields() {
- List<Field<?>> fields = new ArrayList<Field<?>>(getFields());
- Item itemDataSource = getItemDataSource();
-
- if (itemDataSource == null) {
- unbindFields(fields);
- } else {
- bindFields(fields, itemDataSource);
- }
- }
-
- private void unbindFields(List<Field<?>> fields) {
- for (Field<?> field : fields) {
- clearField(field);
- unbind(field);
- field.setParent(null);
- }
- }
-
- private void bindFields(List<Field<?>> fields, Item itemDataSource) {
- for (Field<?> field : fields) {
- if (itemDataSource
- .getItemProperty(getPropertyId(field)) != null) {
- bind(field, getPropertyId(field));
- }
- }
- }
- }
-
- /**
- * Field factory used by default in the editor.
- *
- * Aims to fields of suitable type and with suitable size for use in the
- * editor row.
- */
- public static class EditorFieldFactory
- extends DefaultFieldGroupFieldFactory {
- private static final EditorFieldFactory INSTANCE = new EditorFieldFactory();
-
- protected EditorFieldFactory() {
- }
-
- /**
- * Returns the singleton instance
- *
- * @return the singleton instance
- */
- public static EditorFieldFactory get() {
- return INSTANCE;
- }
-
- @Override
- public <T extends Field> T createField(Class<?> type,
- Class<T> fieldType) {
- T f = super.createField(type, fieldType);
- if (f != null) {
- f.setWidth("100%");
- }
- return f;
- }
-
- @Override
- protected AbstractSelect createCompatibleSelect(
- Class<? extends AbstractSelect> fieldType) {
- if (anySelect(fieldType)) {
- return super.createCompatibleSelect(ComboBox.class);
- }
- return super.createCompatibleSelect(fieldType);
- }
-
- @Override
- protected void populateWithEnumData(AbstractSelect select,
- Class<? extends Enum> enumClass) {
- // Use enums directly and the EnumToStringConverter to be consistent
- // with what is shown in the Grid
- @SuppressWarnings("unchecked")
- EnumSet<?> enumSet = EnumSet.allOf(enumClass);
- for (Object r : enumSet) {
- select.addItem(r);
- }
- }
- }
-
- /**
- * Error handler for the editor
- */
- public interface EditorErrorHandler extends Serializable {
-
- /**
- * Called when an exception occurs while the editor row is being saved
- *
- * @param event
- * An event providing more information about the error
- */
- void commitError(CommitErrorEvent event);
- }
-
- /**
- * ContextClickEvent for the Grid Component.
- *
- * @since 7.6
- */
- public static class GridContextClickEvent extends ContextClickEvent {
-
- private final Object itemId;
- private final int rowIndex;
- private final Object propertyId;
- private final Section section;
-
- public GridContextClickEvent(Grid source,
- MouseEventDetails mouseEventDetails, Section section,
- int rowIndex, Object itemId, Object propertyId) {
- super(source, mouseEventDetails);
- this.itemId = itemId;
- this.propertyId = propertyId;
- this.section = section;
- this.rowIndex = rowIndex;
- }
-
- /**
- * Returns the item id of context clicked row.
- *
- * @return item id of clicked row; <code>null</code> if header or footer
- */
- public Object getItemId() {
- return itemId;
- }
-
- /**
- * Returns property id of clicked column.
- *
- * @return property id
- */
- public Object getPropertyId() {
- return propertyId;
- }
-
- /**
- * Return the clicked section of Grid.
- *
- * @return section of grid
- */
- public Section getSection() {
- return section;
- }
-
- /**
- * Returns the clicked row index relative to Grid section. In the body
- * of the Grid the index is the item index in the Container. Header and
- * Footer rows for index can be fetched with
- * {@link Grid#getHeaderRow(int)} and {@link Grid#getFooterRow(int)}.
- *
- * @return row index in section
- */
- public int getRowIndex() {
- return rowIndex;
- }
-
- @Override
- public Grid getComponent() {
- return (Grid) super.getComponent();
- }
- }
-
- /**
- * An event which is fired when saving the editor fails
- */
- public static class CommitErrorEvent extends Component.Event {
-
- private CommitException cause;
-
- private Set<Column> errorColumns = new HashSet<Column>();
-
- private String userErrorMessage;
-
- public CommitErrorEvent(Grid grid, CommitException cause) {
- super(grid);
- this.cause = cause;
- userErrorMessage = cause.getLocalizedMessage();
- }
-
- /**
- * Retrieves the cause of the failure
- *
- * @return the cause of the failure
- */
- public CommitException getCause() {
- return cause;
- }
-
- @Override
- public Grid getComponent() {
- return (Grid) super.getComponent();
- }
-
- /**
- * Checks if validation exceptions caused this error
- *
- * @return true if the problem was caused by a validation error
- */
- public boolean isValidationFailure() {
- return cause.getCause() instanceof InvalidValueException;
- }
-
- /**
- * Marks that an error indicator should be shown for the editor of a
- * column.
- *
- * @param column
- * the column to show an error for
- */
- public void addErrorColumn(Column column) {
- errorColumns.add(column);
- }
-
- /**
- * Gets all the columns that have been marked as erroneous.
- *
- * @return an umodifiable collection of erroneous columns
- */
- public Collection<Column> getErrorColumns() {
- return Collections.unmodifiableCollection(errorColumns);
- }
-
- /**
- * Gets the error message to show to the user.
- *
- * @return error message to show
- */
- public String getUserErrorMessage() {
- return userErrorMessage;
- }
-
- /**
- * Sets the error message to show to the user.
- *
- * @param userErrorMessage
- * the user error message to set
- */
- public void setUserErrorMessage(String userErrorMessage) {
- this.userErrorMessage = userErrorMessage;
- }
-
- }
-
- /**
- * An event listener for column reorder events in the Grid.
- *
- * @since 7.5.0
- */
- public interface ColumnReorderListener extends Serializable {
-
- /**
- * Called when the columns of the grid have been reordered.
- *
- * @param event
- * An event providing more information
- */
- void columnReorder(ColumnReorderEvent event);
- }
-
- /**
- * An event that is fired when the columns are reordered.
- *
- * @since 7.5.0
- */
- public static class ColumnReorderEvent extends Component.Event {
-
- private final boolean userOriginated;
-
- /**
- *
- * @param source
- * the grid where the event originated from
- * @param userOriginated
- * <code>true</code> if event is a result of user
- * interaction, <code>false</code> if from API call
- */
- public ColumnReorderEvent(Grid source, boolean userOriginated) {
- super(source);
- this.userOriginated = userOriginated;
- }
-
- /**
- * Returns <code>true</code> if the column reorder was done by the user,
- * <code>false</code> if not and it was triggered by server side code.
- *
- * @return <code>true</code> if event is a result of user interaction
- */
- public boolean isUserOriginated() {
- return userOriginated;
- }
-
- }
-
- /**
- * An event listener for column resize events in the Grid.
- *
- * @since 7.6
- */
- public interface ColumnResizeListener extends Serializable {
-
- /**
- * Called when the columns of the grid have been resized.
- *
- * @param event
- * An event providing more information
- */
- void columnResize(ColumnResizeEvent event);
- }
-
- /**
- * An event that is fired when a column is resized, either programmatically
- * or by the user.
- *
- * @since 7.6
- */
- public static class ColumnResizeEvent extends Component.Event {
-
- private final Column column;
- private final boolean userOriginated;
-
- /**
- *
- * @param source
- * the grid where the event originated from
- * @param userOriginated
- * <code>true</code> if event is a result of user
- * interaction, <code>false</code> if from API call
- */
- public ColumnResizeEvent(Grid source, Column column,
- boolean userOriginated) {
- super(source);
- this.column = column;
- this.userOriginated = userOriginated;
- }
-
- /**
- * Returns the column that was resized.
- *
- * @return the resized column.
- */
- public Column getColumn() {
- return column;
- }
-
- /**
- * Returns <code>true</code> if the column resize was done by the user,
- * <code>false</code> if not and it was triggered by server side code.
- *
- * @return <code>true</code> if event is a result of user interaction
- */
- public boolean isUserOriginated() {
- return userOriginated;
- }
-
- }
-
- /**
- * Interface for an editor event listener
- */
- public interface EditorListener extends Serializable {
-
- public static final Method EDITOR_OPEN_METHOD = ReflectTools.findMethod(
- EditorListener.class, "editorOpened", EditorOpenEvent.class);
- public static final Method EDITOR_MOVE_METHOD = ReflectTools.findMethod(
- EditorListener.class, "editorMoved", EditorMoveEvent.class);
- public static final Method EDITOR_CLOSE_METHOD = ReflectTools
- .findMethod(EditorListener.class, "editorClosed",
- EditorCloseEvent.class);
-
- /**
- * Called when an editor is opened
- *
- * @param e
- * an editor open event object
- */
- public void editorOpened(EditorOpenEvent e);
-
- /**
- * Called when an editor is reopened without closing it first
- *
- * @param e
- * an editor move event object
- */
- public void editorMoved(EditorMoveEvent e);
-
- /**
- * Called when an editor is closed
- *
- * @param e
- * an editor close event object
- */
- public void editorClosed(EditorCloseEvent e);
-
- }
-
- /**
- * Base class for editor related events
- */
- public static abstract class EditorEvent extends Component.Event {
-
- private Object itemID;
-
- protected EditorEvent(Grid source, Object itemID) {
- super(source);
- this.itemID = itemID;
- }
-
- /**
- * Get the item (row) for which this editor was opened
- */
- public Object getItem() {
- return itemID;
- }
-
- }
-
- /**
- * This event gets fired when an editor is opened
- */
- public static class EditorOpenEvent extends EditorEvent {
-
- public EditorOpenEvent(Grid source, Object itemID) {
- super(source, itemID);
- }
- }
-
- /**
- * This event gets fired when an editor is opened while another row is being
- * edited (i.e. editor focus moves elsewhere)
- */
- public static class EditorMoveEvent extends EditorEvent {
-
- public EditorMoveEvent(Grid source, Object itemID) {
- super(source, itemID);
- }
- }
-
- /**
- * This event gets fired when an editor is dismissed or closed by other
- * means.
- */
- public static class EditorCloseEvent extends EditorEvent {
-
- public EditorCloseEvent(Grid source, Object itemID) {
- super(source, itemID);
- }
- }
-
- /**
- * Default error handler for the editor
- *
- */
- public class DefaultEditorErrorHandler implements EditorErrorHandler {
-
- @Override
- public void commitError(CommitErrorEvent event) {
- Map<Field<?>, InvalidValueException> invalidFields = event
- .getCause().getInvalidFields();
-
- if (!invalidFields.isEmpty()) {
- Object firstErrorPropertyId = null;
- Field<?> firstErrorField = null;
-
- FieldGroup fieldGroup = event.getCause().getFieldGroup();
- for (Column column : getColumns()) {
- Object propertyId = column.getPropertyId();
- Field<?> field = fieldGroup.getField(propertyId);
- if (invalidFields.keySet().contains(field)) {
- event.addErrorColumn(column);
-
- if (firstErrorPropertyId == null) {
- firstErrorPropertyId = propertyId;
- firstErrorField = field;
- }
- }
- }
-
- /*
- * Validation error, show first failure as
- * "<Column header>: <message>"
- */
- String caption = getColumn(firstErrorPropertyId)
- .getHeaderCaption();
- String message = invalidFields.get(firstErrorField)
- .getLocalizedMessage();
-
- event.setUserErrorMessage(caption + ": " + message);
- } else {
- com.vaadin.server.ErrorEvent.findErrorHandler(Grid.this).error(
- new ConnectorErrorEvent(Grid.this, event.getCause()));
- }
- }
-
- private Object getFirstPropertyId(FieldGroup fieldGroup,
- Set<Field<?>> keySet) {
- for (Column c : getColumns()) {
- Object propertyId = c.getPropertyId();
- Field<?> f = fieldGroup.getField(propertyId);
- if (keySet.contains(f)) {
- return propertyId;
- }
- }
- return null;
- }
- }
-
- /**
- * Selection modes representing built-in {@link SelectionModel
- * SelectionModels} that come bundled with {@link Grid}.
- * <p>
- * Passing one of these enums into
- * {@link Grid#setSelectionMode(SelectionMode)} is equivalent to calling
- * {@link Grid#setSelectionModel(SelectionModel)} with one of the built-in
- * implementations of {@link SelectionModel}.
- *
- * @see Grid#setSelectionMode(SelectionMode)
- * @see Grid#setSelectionModel(SelectionModel)
- */
- public enum SelectionMode {
- /** A SelectionMode that maps to {@link SingleSelectionModel} */
- SINGLE {
- @Override
- protected SelectionModel createModel() {
- return new SingleSelectionModel();
- }
-
- },
-
- /** A SelectionMode that maps to {@link MultiSelectionModel} */
- MULTI {
- @Override
- protected SelectionModel createModel() {
- return new MultiSelectionModel();
- }
- },
-
- /** A SelectionMode that maps to {@link NoSelectionModel} */
- NONE {
- @Override
- protected SelectionModel createModel() {
- return new NoSelectionModel();
- }
- };
-
- protected abstract SelectionModel createModel();
- }
-
- /**
- * The server-side interface that controls Grid's selection state.
- * SelectionModel should extend {@link AbstractGridExtension}.
- */
- public interface SelectionModel extends Serializable, Extension {
-
- /**
- * Interface implemented by selection models which support disabling
- * client side selection while still allowing programmatic selection on
- * the server.
- *
- * @since 7.7.7
- */
- public interface HasUserSelectionAllowed extends SelectionModel {
-
- /**
- * Checks if the user is allowed to change the selection.
- *
- * @return <code>true</code> if the user is allowed to change the
- * selection, <code>false</code> otherwise
- */
- public boolean isUserSelectionAllowed();
-
- /**
- * Sets whether the user is allowed to change the selection.
- *
- * @param userSelectionAllowed
- * <code>true</code> if the user is allowed to change the
- * selection, <code>false</code> otherwise
- */
- public void setUserSelectionAllowed(boolean userSelectionAllowed);
-
- }
-
- /**
- * Checks whether an item is selected or not.
- *
- * @param itemId
- * the item id to check for
- * @return <code>true</code> iff the item is selected
- */
- boolean isSelected(Object itemId);
-
- /**
- * Returns a collection of all the currently selected itemIds.
- *
- * @return a collection of all the currently selected itemIds
- */
- Collection<Object> getSelectedRows();
-
- /**
- * Injects the current {@link Grid} instance into the SelectionModel.
- * This method should usually call the extend method of
- * {@link AbstractExtension}.
- * <p>
- * <em>Note:</em> This method should not be called manually.
- *
- * @param grid
- * the Grid in which the SelectionModel currently is, or
- * <code>null</code> when a selection model is being detached
- * from a Grid.
- */
- void setGrid(Grid grid);
-
- /**
- * Resets the SelectiomModel to an initial state.
- * <p>
- * Most often this means that the selection state is cleared, but
- * implementations are free to interpret the "initial state" as they
- * wish. Some, for example, may want to keep the first selected item as
- * selected.
- */
- void reset();
-
- /**
- * A SelectionModel that supports multiple selections to be made.
- * <p>
- * This interface has a contract of having the same behavior, no matter
- * how the selection model is interacted with. In other words, if
- * something is forbidden to do in e.g. the user interface, it must also
- * be forbidden to do in the server-side and client-side APIs.
- */
- public interface Multi extends SelectionModel {
-
- /**
- * Marks items as selected.
- * <p>
- * This method does not clear any previous selection state, only
- * adds to it.
- *
- * @param itemIds
- * the itemId(s) to mark as selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if all the given itemIds already were
- * selected
- * @throws IllegalArgumentException
- * if the <code>itemIds</code> varargs array is
- * <code>null</code> or given itemIds don't exist in the
- * container of Grid
- * @see #deselect(Object...)
- */
- boolean select(Object... itemIds) throws IllegalArgumentException;
-
- /**
- * Marks items as selected.
- * <p>
- * This method does not clear any previous selection state, only
- * adds to it.
- *
- * @param itemIds
- * the itemIds to mark as selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if all the given itemIds already were
- * selected
- * @throws IllegalArgumentException
- * if <code>itemIds</code> is <code>null</code> or given
- * itemIds don't exist in the container of Grid
- * @see #deselect(Collection)
- */
- boolean select(Collection<?> itemIds)
- throws IllegalArgumentException;
-
- /**
- * Marks items as deselected.
- *
- * @param itemIds
- * the itemId(s) to remove from being selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if none the given itemIds were
- * selected previously
- * @throws IllegalArgumentException
- * if the <code>itemIds</code> varargs array is
- * <code>null</code>
- * @see #select(Object...)
- */
- boolean deselect(Object... itemIds) throws IllegalArgumentException;
-
- /**
- * Marks items as deselected.
- *
- * @param itemIds
- * the itemId(s) to remove from being selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if none the given itemIds were
- * selected previously
- * @throws IllegalArgumentException
- * if <code>itemIds</code> is <code>null</code>
- * @see #select(Collection)
- */
- boolean deselect(Collection<?> itemIds)
- throws IllegalArgumentException;
-
- /**
- * Marks all the items in the current Container as selected
- *
- * @return <code>true</code> iff some items were previously not
- * selected
- * @see #deselectAll()
- */
- boolean selectAll();
-
- /**
- * Marks all the items in the current Container as deselected
- *
- * @return <code>true</code> iff some items were previously selected
- * @see #selectAll()
- */
- boolean deselectAll();
-
- /**
- * Marks items as selected while deselecting all items not in the
- * given Collection.
- *
- * @param itemIds
- * the itemIds to mark as selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if all the given itemIds already were
- * selected
- * @throws IllegalArgumentException
- * if <code>itemIds</code> is <code>null</code> or given
- * itemIds don't exist in the container of Grid
- */
- boolean setSelected(Collection<?> itemIds)
- throws IllegalArgumentException;
-
- /**
- * Marks items as selected while deselecting all items not in the
- * varargs array.
- *
- * @param itemIds
- * the itemIds to mark as selected
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if all the given itemIds already were
- * selected
- * @throws IllegalArgumentException
- * if the <code>itemIds</code> varargs array is
- * <code>null</code> or given itemIds don't exist in the
- * container of Grid
- */
- boolean setSelected(Object... itemIds)
- throws IllegalArgumentException;
- }
-
- /**
- * A SelectionModel that supports for only single rows to be selected at
- * a time.
- * <p>
- * This interface has a contract of having the same behavior, no matter
- * how the selection model is interacted with. In other words, if
- * something is forbidden to do in e.g. the user interface, it must also
- * be forbidden to do in the server-side and client-side APIs.
- */
- public interface Single extends SelectionModel {
-
- /**
- * Marks an item as selected.
- *
- * @param itemId
- * the itemId to mark as selected; <code>null</code> for
- * deselect
- * @return <code>true</code> if the selection state changed.
- * <code>false</code> if the itemId already was selected
- * @throws IllegalStateException
- * if the selection was illegal. One such reason might
- * be that the given id was null, indicating a deselect,
- * but implementation doesn't allow deselecting.
- * re-selecting something
- * @throws IllegalArgumentException
- * if given itemId does not exist in the container of
- * Grid
- */
- boolean select(Object itemId)
- throws IllegalStateException, IllegalArgumentException;
-
- /**
- * Gets the item id of the currently selected item.
- *
- * @return the item id of the currently selected item, or
- * <code>null</code> if nothing is selected
- */
- Object getSelectedRow();
-
- /**
- * Sets whether it's allowed to deselect the selected row through
- * the UI. Deselection is allowed by default.
- *
- * @param deselectAllowed
- * <code>true</code> if the selected row can be
- * deselected without selecting another row instead;
- * otherwise <code>false</code>.
- */
- public void setDeselectAllowed(boolean deselectAllowed);
-
- /**
- * Sets whether it's allowed to deselect the selected row through
- * the UI.
- *
- * @return <code>true</code> if deselection is allowed; otherwise
- * <code>false</code>
- */
- public boolean isDeselectAllowed();
- }
-
- /**
- * A SelectionModel that does not allow for rows to be selected.
- * <p>
- * This interface has a contract of having the same behavior, no matter
- * how the selection model is interacted with. In other words, if the
- * developer is unable to select something programmatically, it is not
- * allowed for the end-user to select anything, either.
- */
- public interface None extends SelectionModel {
-
- /**
- * {@inheritDoc}
- *
- * @return always <code>false</code>.
- */
- @Override
- public boolean isSelected(Object itemId);
-
- /**
- * {@inheritDoc}
- *
- * @return always an empty collection.
- */
- @Override
- public Collection<Object> getSelectedRows();
- }
- }
-
- /**
- * A base class for SelectionModels that contains some of the logic that is
- * reusable.
- */
- public static abstract class AbstractSelectionModel extends
- AbstractGridExtension implements SelectionModel, DataGenerator {
- protected final LinkedHashSet<Object> selection = new LinkedHashSet<Object>();
-
- @Override
- public boolean isSelected(final Object itemId) {
- return selection.contains(itemId);
- }
-
- @Override
- public Collection<Object> getSelectedRows() {
- return new ArrayList<Object>(selection);
- }
-
- @Override
- public void setGrid(final Grid grid) {
- if (grid != null) {
- extend(grid);
- }
- }
-
- /**
- * Sanity check for existence of item id.
- *
- * @param itemId
- * item id to be selected / deselected
- *
- * @throws IllegalArgumentException
- * if item Id doesn't exist in the container of Grid
- */
- protected void checkItemIdExists(Object itemId)
- throws IllegalArgumentException {
- if (!getParentGrid().getContainerDataSource().containsId(itemId)) {
- throw new IllegalArgumentException("Given item id (" + itemId
- + ") does not exist in the container");
- }
- }
-
- /**
- * Sanity check for existence of item ids in given collection.
- *
- * @param itemIds
- * item id collection to be selected / deselected
- *
- * @throws IllegalArgumentException
- * if at least one item id doesn't exist in the container of
- * Grid
- */
- protected void checkItemIdsExist(Collection<?> itemIds)
- throws IllegalArgumentException {
- for (Object itemId : itemIds) {
- checkItemIdExists(itemId);
- }
- }
-
- /**
- * Fires a {@link SelectionEvent} to all the {@link SelectionListener
- * SelectionListeners} currently added to the Grid in which this
- * SelectionModel is.
- * <p>
- * Note that this is only a helper method, and routes the call all the
- * way to Grid. A {@link SelectionModel} is not a
- * {@link SelectionNotifier}
- *
- * @param oldSelection
- * the complete {@link Collection} of the itemIds that were
- * selected <em>before</em> this event happened
- * @param newSelection
- * the complete {@link Collection} of the itemIds that are
- * selected <em>after</em> this event happened
- */
- protected void fireSelectionEvent(final Collection<Object> oldSelection,
- final Collection<Object> newSelection) {
- getParentGrid().fireSelectionEvent(oldSelection, newSelection);
- }
-
- @Override
- public void generateData(Object itemId, Item item, JsonObject rowData) {
- if (isSelected(itemId)) {
- rowData.put(GridState.JSONKEY_SELECTED, true);
- }
- }
-
- @Override
- public void destroyData(Object itemId) {
- // NO-OP
- }
-
- @Override
- protected Object getItemId(String rowKey) {
- return rowKey != null ? super.getItemId(rowKey) : null;
- }
- }
-
- /**
- * A default implementation of a {@link SelectionModel.Single}
- */
- public static class SingleSelectionModel extends AbstractSelectionModel
- implements SelectionModel.Single, HasUserSelectionAllowed {
-
- @Override
- protected void extend(AbstractClientConnector target) {
- super.extend(target);
- registerRpc(new SingleSelectionModelServerRpc() {
-
- @Override
- public void select(String rowKey) {
- if (!isUserSelectionAllowed()) {
- throw new IllegalStateException(
- "Client tried to select '" + rowKey
- + "' although user selection is disallowed");
- }
- SingleSelectionModel.this.select(getItemId(rowKey), false);
- }
- });
- }
-
- @Override
- public boolean select(final Object itemId) {
- return select(itemId, true);
- }
-
- protected boolean select(final Object itemId, boolean refresh) {
- if (itemId == null) {
- return deselect(getSelectedRow());
- }
-
- checkItemIdExists(itemId);
-
- final Object selectedRow = getSelectedRow();
- final boolean modified = selection.add(itemId);
- if (modified) {
- final Collection<Object> deselected;
- if (selectedRow != null) {
- deselectInternal(selectedRow, false, true);
- deselected = Collections.singleton(selectedRow);
- } else {
- deselected = Collections.emptySet();
- }
-
- fireSelectionEvent(deselected, selection);
- }
-
- if (refresh) {
- refreshRow(itemId);
- }
-
- return modified;
- }
-
- private boolean deselect(final Object itemId) {
- return deselectInternal(itemId, true, true);
- }
-
- private boolean deselectInternal(final Object itemId,
- boolean fireEventIfNeeded, boolean refresh) {
- final boolean modified = selection.remove(itemId);
- if (modified) {
- if (refresh) {
- refreshRow(itemId);
- }
- if (fireEventIfNeeded) {
- fireSelectionEvent(Collections.singleton(itemId),
- Collections.emptySet());
- }
- }
- return modified;
- }
-
- @Override
- public Object getSelectedRow() {
- if (selection.isEmpty()) {
- return null;
- } else {
- return selection.iterator().next();
- }
- }
-
- /**
- * Resets the selection state.
- * <p>
- * If an item is selected, it will become deselected.
- */
- @Override
- public void reset() {
- deselect(getSelectedRow());
- }
-
- @Override
- public void setDeselectAllowed(boolean deselectAllowed) {
- getState().deselectAllowed = deselectAllowed;
- }
-
- @Override
- public boolean isDeselectAllowed() {
- return getState().deselectAllowed;
- }
-
- @Override
- protected SingleSelectionModelState getState() {
- return (SingleSelectionModelState) super.getState();
- }
-
- @Override
- protected SingleSelectionModelState getState(boolean markAsDirty) {
- return (SingleSelectionModelState) super.getState(markAsDirty);
- }
-
- @Override
- public boolean isUserSelectionAllowed() {
- return getState(false).userSelectionAllowed;
- }
-
- @Override
- public void setUserSelectionAllowed(boolean userSelectionAllowed) {
- getState().userSelectionAllowed = userSelectionAllowed;
- }
- }
-
- /**
- * A default implementation for a {@link SelectionModel.None}
- */
- public static class NoSelectionModel extends AbstractSelectionModel
- implements SelectionModel.None {
-
- @Override
- public boolean isSelected(final Object itemId) {
- return false;
- }
-
- @Override
- public Collection<Object> getSelectedRows() {
- return Collections.emptyList();
- }
-
- /**
- * Semantically resets the selection model.
- * <p>
- * Effectively a no-op.
- */
- @Override
- public void reset() {
- // NOOP
- }
-
- }
-
- /**
- * A default implementation of a {@link SelectionModel.Multi}
- */
- public static class MultiSelectionModel extends AbstractSelectionModel
- implements SelectionModel.Multi,
- SelectionModel.HasUserSelectionAllowed {
-
- /**
- * The default selection size limit.
- *
- * @see #setSelectionLimit(int)
- */
- public static final int DEFAULT_MAX_SELECTIONS = 1000;
-
- private int selectionLimit = DEFAULT_MAX_SELECTIONS;
-
- @Override
- protected void extend(AbstractClientConnector target) {
- super.extend(target);
- registerRpc(new MultiSelectionModelServerRpc() {
-
- @Override
- public void select(List<String> rowKeys) {
- if (!isUserSelectionAllowed()) {
- throw new IllegalStateException(
- "Client tried to select '" + rowKeys
- + "' although user selection is disallowed");
- }
-
- List<Object> items = new ArrayList<Object>();
- for (String rowKey : rowKeys) {
- items.add(getItemId(rowKey));
- }
- MultiSelectionModel.this.select(items, false);
- }
-
- @Override
- public void deselect(List<String> rowKeys) {
- if (!isUserSelectionAllowed()) {
- throw new IllegalStateException(
- "Client tried to deselect '" + rowKeys
- + "' although user selection is disallowed");
- }
-
- List<Object> items = new ArrayList<Object>();
- for (String rowKey : rowKeys) {
- items.add(getItemId(rowKey));
- }
- MultiSelectionModel.this.deselect(items, false);
- }
-
- @Override
- public void selectAll() {
- if (!isUserSelectionAllowed()) {
- throw new IllegalStateException(
- "Client tried to select all although user selection is disallowed");
- }
-
- MultiSelectionModel.this.selectAll(false);
- }
-
- @Override
- public void deselectAll() {
- if (!isUserSelectionAllowed()) {
- throw new IllegalStateException(
- "Client tried to deselect all although user selection is disallowed");
- }
-
- MultiSelectionModel.this.deselectAll(false);
- }
- });
- }
-
- @Override
- public boolean select(final Object... itemIds)
- throws IllegalArgumentException {
- if (itemIds != null) {
- // select will fire the event
- return select(Arrays.asList(itemIds));
- } else {
- throw new IllegalArgumentException(
- "Vararg array of itemIds may not be null");
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * All items might not be selected if the limit set using
- * {@link #setSelectionLimit(int)} is exceeded.
- */
- @Override
- public boolean select(final Collection<?> itemIds)
- throws IllegalArgumentException {
- return select(itemIds, true);
- }
-
- protected boolean select(final Collection<?> itemIds, boolean refresh) {
- if (itemIds == null) {
- throw new IllegalArgumentException("itemIds may not be null");
- }
-
- // Sanity check
- checkItemIdsExist(itemIds);
-
- final boolean selectionWillChange = !selection.containsAll(itemIds)
- && selection.size() < selectionLimit;
- if (selectionWillChange) {
- final HashSet<Object> oldSelection = new HashSet<Object>(
- selection);
- if (selection.size() + itemIds.size() >= selectionLimit) {
- // Add one at a time if there's a risk of overflow
- Iterator<?> iterator = itemIds.iterator();
- while (iterator.hasNext()
- && selection.size() < selectionLimit) {
- selection.add(iterator.next());
- }
- } else {
- selection.addAll(itemIds);
- }
- fireSelectionEvent(oldSelection, selection);
- }
-
- updateAllSelectedState();
-
- if (refresh) {
- for (Object itemId : itemIds) {
- refreshRow(itemId);
- }
- }
-
- return selectionWillChange;
- }
-
- /**
- * Sets the maximum number of rows that can be selected at once. This is
- * a mechanism to prevent exhausting server memory in situations where
- * users select lots of rows. If the limit is reached, newly selected
- * rows will not become recorded.
- * <p>
- * Old selections are not discarded if the current number of selected
- * row exceeds the new limit.
- * <p>
- * The default limit is {@value #DEFAULT_MAX_SELECTIONS} rows.
- *
- * @param selectionLimit
- * the non-negative selection limit to set
- * @throws IllegalArgumentException
- * if the limit is negative
- */
- public void setSelectionLimit(int selectionLimit) {
- if (selectionLimit < 0) {
- throw new IllegalArgumentException(
- "The selection limit must be non-negative");
- }
- this.selectionLimit = selectionLimit;
- }
-
- /**
- * Gets the selection limit.
- *
- * @see #setSelectionLimit(int)
- *
- * @return the selection limit
- */
- public int getSelectionLimit() {
- return selectionLimit;
- }
-
- @Override
- public boolean deselect(final Object... itemIds)
- throws IllegalArgumentException {
- if (itemIds != null) {
- // deselect will fire the event
- return deselect(Arrays.asList(itemIds));
- } else {
- throw new IllegalArgumentException(
- "Vararg array of itemIds may not be null");
- }
- }
-
- @Override
- public boolean deselect(final Collection<?> itemIds)
- throws IllegalArgumentException {
- return deselect(itemIds, true);
- }
-
- protected boolean deselect(final Collection<?> itemIds,
- boolean refresh) {
- if (itemIds == null) {
- throw new IllegalArgumentException("itemIds may not be null");
- }
-
- final boolean hasCommonElements = !Collections.disjoint(itemIds,
- selection);
- if (hasCommonElements) {
- final HashSet<Object> oldSelection = new HashSet<Object>(
- selection);
- selection.removeAll(itemIds);
- fireSelectionEvent(oldSelection, selection);
- }
-
- updateAllSelectedState();
-
- if (refresh) {
- for (Object itemId : itemIds) {
- refreshRow(itemId);
- }
- }
-
- return hasCommonElements;
- }
-
- @Override
- public boolean selectAll() {
- return selectAll(true);
- }
-
- protected boolean selectAll(boolean refresh) {
- // select will fire the event
- final Indexed container = getParentGrid().getContainerDataSource();
- if (container != null) {
- return select(container.getItemIds(), refresh);
- } else if (selection.isEmpty()) {
- return false;
- } else {
- /*
- * this should never happen (no container but has a selection),
- * but I guess the only theoretically correct course of
- * action...
- */
- return deselectAll(false);
- }
- }
-
- @Override
- public boolean deselectAll() {
- return deselectAll(true);
- }
-
- protected boolean deselectAll(boolean refresh) {
- // deselect will fire the event
- return deselect(getSelectedRows(), refresh);
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * The returned Collection is in <strong>order of selection</strong>
- * – the item that was first selected will be first in the
- * collection, and so on. Should an item have been selected twice
- * without being deselected in between, it will have remained in its
- * original position.
- */
- @Override
- public Collection<Object> getSelectedRows() {
- // overridden only for JavaDoc
- return super.getSelectedRows();
- }
-
- /**
- * Resets the selection model.
- * <p>
- * Equivalent to calling {@link #deselectAll()}
- */
- @Override
- public void reset() {
- deselectAll();
- }
-
- @Override
- public boolean setSelected(Collection<?> itemIds)
- throws IllegalArgumentException {
- if (itemIds == null) {
- throw new IllegalArgumentException("itemIds may not be null");
- }
-
- checkItemIdsExist(itemIds);
-
- boolean changed = false;
- Set<Object> selectedRows = new HashSet<Object>(itemIds);
- final Collection<Object> oldSelection = getSelectedRows();
- Set<Object> added = getDifference(selectedRows, selection);
- if (!added.isEmpty()) {
- changed = true;
- selection.addAll(added);
- for (Object id : added) {
- refreshRow(id);
- }
- }
-
- Set<Object> removed = getDifference(selection, selectedRows);
- if (!removed.isEmpty()) {
- changed = true;
- selection.removeAll(removed);
- for (Object id : removed) {
- refreshRow(id);
- }
- }
-
- if (changed) {
- fireSelectionEvent(oldSelection, selection);
- }
-
- updateAllSelectedState();
-
- return changed;
- }
-
- /**
- * Compares two sets and returns a set containing all values that are
- * present in the first, but not in the second.
- *
- * @param set1
- * first item set
- * @param set2
- * second item set
- * @return all values from set1 which are not present in set2
- */
- private static Set<Object> getDifference(Set<Object> set1,
- Set<Object> set2) {
- Set<Object> diff = new HashSet<Object>(set1);
- diff.removeAll(set2);
- return diff;
- }
-
- @Override
- public boolean setSelected(Object... itemIds)
- throws IllegalArgumentException {
- if (itemIds != null) {
- return setSelected(Arrays.asList(itemIds));
- } else {
- throw new IllegalArgumentException(
- "Vararg array of itemIds may not be null");
- }
- }
-
- private void updateAllSelectedState() {
- int totalRowCount = getParentGrid().datasource.size();
- int rows = Math.min(totalRowCount, selectionLimit);
- if (totalRowCount == 0) {
- getState().allSelected = false;
- } else {
- getState().allSelected = selection.size() >= rows;
- }
- }
-
- @Override
- protected MultiSelectionModelState getState() {
- return (MultiSelectionModelState) super.getState();
- }
-
- @Override
- protected MultiSelectionModelState getState(boolean markAsDirty) {
- return (MultiSelectionModelState) super.getState(markAsDirty);
- }
-
- @Override
- public boolean isUserSelectionAllowed() {
- return getState(false).userSelectionAllowed;
- }
-
- @Override
- public void setUserSelectionAllowed(boolean userSelectionAllowed) {
- getState().userSelectionAllowed = userSelectionAllowed;
- }
- }
-
- /**
- * A data class which contains information which identifies a row in a
- * {@link Grid}.
- * <p>
- * Since this class follows the <code>Flyweight</code>-pattern any instance
- * of this object is subject to change without the user knowing it and so
- * should not be stored anywhere outside of the method providing these
- * instances.
- */
- public static class RowReference implements Serializable {
- private final Grid grid;
-
- private Object itemId;
-
- /**
- * Creates a new row reference for the given grid.
- *
- * @param grid
- * the grid that the row belongs to
- */
- public RowReference(Grid grid) {
- this.grid = grid;
- }
-
- /**
- * Sets the identifying information for this row
- *
- * @param itemId
- * the item id of the row
- */
- public void set(Object itemId) {
- this.itemId = itemId;
- }
-
- /**
- * Gets the grid that contains the referenced row.
- *
- * @return the grid that contains referenced row
- */
- public Grid getGrid() {
- return grid;
- }
-
- /**
- * Gets the item id of the row.
- *
- * @return the item id of the row
- */
- public Object getItemId() {
- return itemId;
- }
-
- /**
- * Gets the item for the row.
- *
- * @return the item for the row
- */
- public Item getItem() {
- return grid.getContainerDataSource().getItem(itemId);
- }
- }
-
- /**
- * A data class which contains information which identifies a cell in a
- * {@link Grid}.
- * <p>
- * Since this class follows the <code>Flyweight</code>-pattern any instance
- * of this object is subject to change without the user knowing it and so
- * should not be stored anywhere outside of the method providing these
- * instances.
- */
- public static class CellReference implements Serializable {
- private final RowReference rowReference;
-
- private Object propertyId;
-
- public CellReference(RowReference rowReference) {
- this.rowReference = rowReference;
- }
-
- /**
- * Sets the identifying information for this cell
- *
- * @param propertyId
- * the property id of the column
- */
- public void set(Object propertyId) {
- this.propertyId = propertyId;
- }
-
- /**
- * Gets the grid that contains the referenced cell.
- *
- * @return the grid that contains referenced cell
- */
- public Grid getGrid() {
- return rowReference.getGrid();
- }
-
- /**
- * @return the property id of the column
- */
- public Object getPropertyId() {
- return propertyId;
- }
-
- /**
- * @return the property for the cell
- */
- public Property<?> getProperty() {
- return getItem().getItemProperty(propertyId);
- }
-
- /**
- * Gets the item id of the row of the cell.
- *
- * @return the item id of the row
- */
- public Object getItemId() {
- return rowReference.getItemId();
- }
-
- /**
- * Gets the item for the row of the cell.
- *
- * @return the item for the row
- */
- public Item getItem() {
- return rowReference.getItem();
- }
-
- /**
- * Gets the value of the cell.
- *
- * @return the value of the cell
- */
- public Object getValue() {
- return getProperty().getValue();
- }
- }
-
- /**
- * A callback interface for generating custom style names for Grid rows.
- *
- * @see Grid#setRowStyleGenerator(RowStyleGenerator)
- */
- public interface RowStyleGenerator extends Serializable {
-
- /**
- * Called by Grid to generate a style name for a row.
- *
- * @param row
- * the row to generate a style for
- * @return the style name to add to this row, or {@code null} to not set
- * any style
- */
- public String getStyle(RowReference row);
- }
-
- /**
- * A callback interface for generating custom style names for Grid cells.
- *
- * @see Grid#setCellStyleGenerator(CellStyleGenerator)
- */
- public interface CellStyleGenerator extends Serializable {
-
- /**
- * Called by Grid to generate a style name for a column.
- *
- * @param cell
- * the cell to generate a style for
- * @return the style name to add to this cell, or {@code null} to not
- * set any style
- */
- public String getStyle(CellReference cell);
- }
-
- /**
- * A callback interface for generating optional descriptions (tooltips) for
- * Grid rows. If a description is generated for a row, it is used for all
- * the cells in the row for which a {@link CellDescriptionGenerator cell
- * description} is not generated.
- *
- * @see Grid#setRowDescriptionGenerator
- *
- * @since 7.6
- */
- public interface RowDescriptionGenerator extends Serializable {
-
- /**
- * Called by Grid to generate a description (tooltip) for a row. The
- * description may contain HTML which is rendered directly; if this is
- * not desired the returned string must be escaped by the implementing
- * method.
- *
- * @param row
- * the row to generate a description for
- * @return the row description or {@code null} for no description
- */
- public String getDescription(RowReference row);
- }
-
- /**
- * A callback interface for generating optional descriptions (tooltips) for
- * Grid cells. If a cell has both a {@link RowDescriptionGenerator row
- * description} and a cell description, the latter has precedence.
- *
- * @see Grid#setCellDescriptionGenerator(CellDescriptionGenerator)
- *
- * @since 7.6
- */
- public interface CellDescriptionGenerator extends Serializable {
-
- /**
- * Called by Grid to generate a description (tooltip) for a cell. The
- * description may contain HTML which is rendered directly; if this is
- * not desired the returned string must be escaped by the implementing
- * method.
- *
- * @param cell
- * the cell to generate a description for
- * @return the cell description or {@code null} for no description
- */
- public String getDescription(CellReference cell);
- }
-
- /**
- * Class for generating all row and cell related data for the essential
- * parts of Grid.
- */
- private class RowDataGenerator implements DataGenerator {
-
- private void put(String key, String value, JsonObject object) {
- if (value != null && !value.isEmpty()) {
- object.put(key, value);
- }
- }
-
- @Override
- public void generateData(Object itemId, Item item, JsonObject rowData) {
- RowReference row = new RowReference(Grid.this);
- row.set(itemId);
-
- if (rowStyleGenerator != null) {
- String style = rowStyleGenerator.getStyle(row);
- put(GridState.JSONKEY_ROWSTYLE, style, rowData);
- }
-
- if (rowDescriptionGenerator != null) {
- String description = rowDescriptionGenerator
- .getDescription(row);
- put(GridState.JSONKEY_ROWDESCRIPTION, description, rowData);
-
- }
-
- JsonObject cellStyles = Json.createObject();
- JsonObject cellData = Json.createObject();
- JsonObject cellDescriptions = Json.createObject();
-
- CellReference cell = new CellReference(row);
-
- for (Column column : getColumns()) {
- cell.set(column.getPropertyId());
-
- writeData(cell, cellData);
- writeStyles(cell, cellStyles);
- writeDescriptions(cell, cellDescriptions);
- }
-
- if (cellDescriptionGenerator != null
- && cellDescriptions.keys().length > 0) {
- rowData.put(GridState.JSONKEY_CELLDESCRIPTION,
- cellDescriptions);
- }
-
- if (cellStyleGenerator != null && cellStyles.keys().length > 0) {
- rowData.put(GridState.JSONKEY_CELLSTYLES, cellStyles);
- }
-
- rowData.put(GridState.JSONKEY_DATA, cellData);
- }
-
- private void writeStyles(CellReference cell, JsonObject styles) {
- if (cellStyleGenerator != null) {
- String style = cellStyleGenerator.getStyle(cell);
- put(columnKeys.key(cell.getPropertyId()), style, styles);
- }
- }
-
- private void writeDescriptions(CellReference cell,
- JsonObject descriptions) {
- if (cellDescriptionGenerator != null) {
- String description = cellDescriptionGenerator
- .getDescription(cell);
- put(columnKeys.key(cell.getPropertyId()), description,
- descriptions);
- }
- }
-
- private void writeData(CellReference cell, JsonObject data) {
- Column column = getColumn(cell.getPropertyId());
- Converter<?, ?> converter = column.getConverter();
- Renderer<?> renderer = column.getRenderer();
-
- Item item = cell.getItem();
- Property itemProperty = item.getItemProperty(cell.getPropertyId());
- Object modelValue = itemProperty == null ? null
- : itemProperty.getValue();
-
- data.put(columnKeys.key(cell.getPropertyId()), AbstractRenderer
- .encodeValue(modelValue, renderer, converter, getLocale()));
- }
-
- @Override
- public void destroyData(Object itemId) {
- // NO-OP
- }
- }
-
- /**
- * Abstract base class for Grid header and footer sections.
- *
- * @since 7.6
- * @param <ROWTYPE>
- * the type of the rows in the section
- */
- public abstract static class StaticSection<ROWTYPE extends StaticSection.StaticRow<?>>
- implements Serializable {
-
- /**
- * Abstract base class for Grid header and footer rows.
- *
- * @param <CELLTYPE>
- * the type of the cells in the row
- */
- public abstract static class StaticRow<CELLTYPE extends StaticCell>
- implements Serializable {
-
- private RowState rowState = new RowState();
- protected StaticSection<?> section;
- private Map<Object, CELLTYPE> cells = new LinkedHashMap<Object, CELLTYPE>();
- private Map<Set<CELLTYPE>, CELLTYPE> cellGroups = new HashMap<Set<CELLTYPE>, CELLTYPE>();
-
- protected StaticRow(StaticSection<?> section) {
- this.section = section;
- }
-
- protected void addCell(Object propertyId) {
- CELLTYPE cell = createCell();
- cell.setColumnId(
- section.grid.getColumn(propertyId).getState().id);
- cells.put(propertyId, cell);
- rowState.cells.add(cell.getCellState());
- }
-
- protected void removeCell(Object propertyId) {
- CELLTYPE cell = cells.remove(propertyId);
- if (cell != null) {
- Set<CELLTYPE> cellGroupForCell = getCellGroupForCell(cell);
- if (cellGroupForCell != null) {
- removeCellFromGroup(cell, cellGroupForCell);
- } else {
- cell.detach();
- }
- rowState.cells.remove(cell.getCellState());
- }
- }
-
- private void removeCellFromGroup(CELLTYPE cell,
- Set<CELLTYPE> cellGroup) {
- String columnId = cell.getColumnId();
- for (Set<String> group : rowState.cellGroups.keySet()) {
- if (group.contains(columnId)) {
- if (group.size() > 2) {
- // Update map key correctly
- CELLTYPE mergedCell = cellGroups.remove(cellGroup);
- cellGroup.remove(cell);
- cellGroups.put(cellGroup, mergedCell);
- group.remove(columnId);
- } else {
- // Only one cell remaining in the group, disband it
- // The contents of the group if removed
- rowState.cellGroups.remove(group);
- CELLTYPE mergedCell = cellGroups.remove(cellGroup);
- mergedCell.detach();
- }
- return;
- }
- }
- }
-
- /**
- * Creates and returns a new instance of the cell type.
- *
- * @return the created cell
- */
- protected abstract CELLTYPE createCell();
-
- protected RowState getRowState() {
- return rowState;
- }
-
- /**
- * Returns the cell for the given property id on this row. If the
- * column is merged returned cell is the cell for the whole group.
- *
- * @param propertyId
- * the property id of the column
- * @return the cell for the given property, merged cell for merged
- * properties, null if not found
- */
- public CELLTYPE getCell(Object propertyId) {
- CELLTYPE cell = cells.get(propertyId);
- Set<CELLTYPE> cellGroup = getCellGroupForCell(cell);
- if (cellGroup != null) {
- cell = cellGroups.get(cellGroup);
- }
- return cell;
- }
-
- /**
- * Merges columns cells in a row.
- *
- * @param propertyIds
- * The property ids of columns to merge
- * @return The remaining visible cell after the merge
- */
- public CELLTYPE join(Object... propertyIds) {
- if (propertyIds.length < 2) {
- throw new IllegalArgumentException(
- "You need to merge at least 2 properties");
- }
-
- Set<CELLTYPE> cells = new HashSet<CELLTYPE>();
- for (int i = 0; i < propertyIds.length; ++i) {
- cells.add(getCell(propertyIds[i]));
- }
-
- return join(cells);
- }
-
- /**
- * Merges columns cells in a row.
- *
- * @param cells
- * The cells to merge. Must be from the same row.
- * @return The remaining visible cell after the merge
- */
- public CELLTYPE join(CELLTYPE... cells) {
- if (cells.length < 2) {
- throw new IllegalArgumentException(
- "You need to merge at least 2 cells");
- }
-
- return join(new HashSet<CELLTYPE>(Arrays.asList(cells)));
- }
-
- protected CELLTYPE join(Set<CELLTYPE> cells) {
- for (CELLTYPE cell : cells) {
- if (getCellGroupForCell(cell) != null) {
- throw new IllegalArgumentException(
- "Cell already merged");
- } else if (!this.cells.containsValue(cell)) {
- throw new IllegalArgumentException(
- "Cell does not exist on this row");
- }
- }
-
- // Create new cell data for the group
- CELLTYPE newCell = createCell();
-
- Set<String> columnGroup = new HashSet<String>();
- for (CELLTYPE cell : cells) {
- columnGroup.add(cell.getColumnId());
- }
- rowState.cellGroups.put(columnGroup, newCell.getCellState());
- cellGroups.put(cells, newCell);
- return newCell;
- }
-
- private Set<CELLTYPE> getCellGroupForCell(CELLTYPE cell) {
- for (Set<CELLTYPE> group : cellGroups.keySet()) {
- if (group.contains(cell)) {
- return group;
- }
- }
- return null;
- }
-
- /**
- * Returns the custom style name for this row.
- *
- * @return the style name or null if no style name has been set
- */
- public String getStyleName() {
- return getRowState().styleName;
- }
-
- /**
- * Sets a custom style name for this row.
- *
- * @param styleName
- * the style name to set or null to not use any style
- * name
- */
- public void setStyleName(String styleName) {
- getRowState().styleName = styleName;
- }
-
- /**
- * Writes the declarative design to the given table row element.
- *
- * @since 7.5.0
- * @param trElement
- * Element to write design to
- * @param designContext
- * the design context
- */
- protected void writeDesign(Element trElement,
- DesignContext designContext) {
- Set<CELLTYPE> visited = new HashSet<CELLTYPE>();
- for (Grid.Column column : section.grid.getColumns()) {
- CELLTYPE cell = getCell(column.getPropertyId());
- if (visited.contains(cell)) {
- continue;
- }
- visited.add(cell);
-
- Element cellElement = trElement
- .appendElement(getCellTagName());
- cell.writeDesign(cellElement, designContext);
-
- for (Entry<Set<CELLTYPE>, CELLTYPE> entry : cellGroups
- .entrySet()) {
- if (entry.getValue() == cell) {
- cellElement.attr("colspan",
- "" + entry.getKey().size());
- break;
- }
- }
- }
- }
-
- /**
- * Reads the declarative design from the given table row element.
- *
- * @since 7.5.0
- * @param trElement
- * Element to read design from
- * @param designContext
- * the design context
- * @throws DesignException
- * if the given table row contains unexpected children
- */
- protected void readDesign(Element trElement,
- DesignContext designContext) throws DesignException {
- Elements cellElements = trElement.children();
- int totalColSpans = 0;
- for (int i = 0; i < cellElements.size(); ++i) {
- Element element = cellElements.get(i);
- if (!element.tagName().equals(getCellTagName())) {
- throw new DesignException(
- "Unexpected element in tr while expecting "
- + getCellTagName() + ": "
- + element.tagName());
- }
-
- int columnIndex = i + totalColSpans;
-
- int colspan = DesignAttributeHandler.readAttribute(
- "colspan", element.attributes(), 1, int.class);
-
- Set<CELLTYPE> cells = new HashSet<CELLTYPE>();
- for (int c = 0; c < colspan; ++c) {
- cells.add(getCell(section.grid.getColumns()
- .get(columnIndex + c).getPropertyId()));
- }
-
- if (colspan > 1) {
- totalColSpans += colspan - 1;
- join(cells).readDesign(element, designContext);
- } else {
- cells.iterator().next().readDesign(element,
- designContext);
- }
- }
- }
-
- abstract protected String getCellTagName();
-
- void detach() {
- for (CELLTYPE cell : cells.values()) {
- cell.detach();
- }
- for (CELLTYPE cell : cellGroups.values()) {
- cell.detach();
- }
- }
- }
-
- /**
- * A header or footer cell. Has a simple textual caption.
- */
- abstract static class StaticCell implements Serializable {
-
- private CellState cellState = new CellState();
- private StaticRow<?> row;
-
- protected StaticCell(StaticRow<?> row) {
- this.row = row;
- }
-
- void setColumnId(String id) {
- cellState.columnId = id;
- }
-
- String getColumnId() {
- return cellState.columnId;
- }
-
- /**
- * Gets the row where this cell is.
- *
- * @return row for this cell
- */
- public StaticRow<?> getRow() {
- return row;
- }
-
- protected CellState getCellState() {
- return cellState;
- }
-
- /**
- * Sets the text displayed in this cell.
- *
- * @param text
- * a plain text caption
- */
- public void setText(String text) {
- removeComponentIfPresent();
- cellState.text = text;
- cellState.type = GridStaticCellType.TEXT;
- row.section.markAsDirty();
- }
-
- /**
- * Returns the text displayed in this cell.
- *
- * @return the plain text caption
- */
- public String getText() {
- if (cellState.type != GridStaticCellType.TEXT) {
- throw new IllegalStateException(
- "Cannot fetch Text from a cell with type "
- + cellState.type);
- }
- return cellState.text;
- }
-
- /**
- * Returns the HTML content displayed in this cell.
- *
- * @return the html
- *
- */
- public String getHtml() {
- if (cellState.type != GridStaticCellType.HTML) {
- throw new IllegalStateException(
- "Cannot fetch HTML from a cell with type "
- + cellState.type);
- }
- return cellState.html;
- }
-
- /**
- * Sets the HTML content displayed in this cell.
- *
- * @param html
- * the html to set
- */
- public void setHtml(String html) {
- removeComponentIfPresent();
- cellState.html = html;
- cellState.type = GridStaticCellType.HTML;
- row.section.markAsDirty();
- }
-
- /**
- * Returns the component displayed in this cell.
- *
- * @return the component
- */
- public Component getComponent() {
- if (cellState.type != GridStaticCellType.WIDGET) {
- throw new IllegalStateException(
- "Cannot fetch Component from a cell with type "
- + cellState.type);
- }
- return (Component) cellState.connector;
- }
-
- /**
- * Sets the component displayed in this cell.
- *
- * @param component
- * the component to set
- */
- public void setComponent(Component component) {
- removeComponentIfPresent();
- component.setParent(row.section.grid);
- cellState.connector = component;
- cellState.type = GridStaticCellType.WIDGET;
- row.section.markAsDirty();
- }
-
- /**
- * Returns the type of content stored in this cell.
- *
- * @return cell content type
- */
- public GridStaticCellType getCellType() {
- return cellState.type;
- }
-
- /**
- * Returns the custom style name for this cell.
- *
- * @return the style name or null if no style name has been set
- */
- public String getStyleName() {
- return cellState.styleName;
- }
-
- /**
- * Sets a custom style name for this cell.
- *
- * @param styleName
- * the style name to set or null to not use any style
- * name
- */
- public void setStyleName(String styleName) {
- cellState.styleName = styleName;
- row.section.markAsDirty();
- }
-
- private void removeComponentIfPresent() {
- Component component = (Component) cellState.connector;
- if (component != null) {
- component.setParent(null);
- cellState.connector = null;
- }
- }
-
- /**
- * Writes the declarative design to the given table cell element.
- *
- * @since 7.5.0
- * @param cellElement
- * Element to write design to
- * @param designContext
- * the design context
- */
- protected void writeDesign(Element cellElement,
- DesignContext designContext) {
- switch (cellState.type) {
- case TEXT:
- cellElement.attr("plain-text", true);
- cellElement.appendText(getText());
- break;
- case HTML:
- cellElement.append(getHtml());
- break;
- case WIDGET:
- cellElement.appendChild(
- designContext.createElement(getComponent()));
- break;
- }
- }
-
- /**
- * Reads the declarative design from the given table cell element.
- *
- * @since 7.5.0
- * @param cellElement
- * Element to read design from
- * @param designContext
- * the design context
- */
- protected void readDesign(Element cellElement,
- DesignContext designContext) {
- if (!cellElement.hasAttr("plain-text")) {
- if (cellElement.children().size() > 0
- && cellElement.child(0).tagName().contains("-")) {
- setComponent(
- designContext.readDesign(cellElement.child(0)));
- } else {
- setHtml(cellElement.html());
- }
- } else {
- // text – need to unescape HTML entities
- setText(DesignFormatter
- .decodeFromTextNode(cellElement.html()));
- }
- }
-
- void detach() {
- removeComponentIfPresent();
- }
- }
-
- protected Grid grid;
- protected List<ROWTYPE> rows = new ArrayList<ROWTYPE>();
-
- /**
- * Sets the visibility of the whole section.
- *
- * @param visible
- * true to show this section, false to hide
- */
- public void setVisible(boolean visible) {
- if (getSectionState().visible != visible) {
- getSectionState().visible = visible;
- markAsDirty();
- }
- }
-
- /**
- * Returns the visibility of this section.
- *
- * @return true if visible, false otherwise.
- */
- public boolean isVisible() {
- return getSectionState().visible;
- }
-
- /**
- * Removes the row at the given position.
- *
- * @param rowIndex
- * the position of the row
- *
- * @throws IllegalArgumentException
- * if no row exists at given index
- * @see #removeRow(StaticRow)
- * @see #addRowAt(int)
- * @see #appendRow()
- * @see #prependRow()
- */
- public ROWTYPE removeRow(int rowIndex) {
- if (rowIndex >= rows.size() || rowIndex < 0) {
- throw new IllegalArgumentException(
- "No row at given index " + rowIndex);
- }
- ROWTYPE row = rows.remove(rowIndex);
- row.detach();
- getSectionState().rows.remove(rowIndex);
-
- markAsDirty();
- return row;
- }
-
- /**
- * Removes the given row from the section.
- *
- * @param row
- * the row to be removed
- *
- * @throws IllegalArgumentException
- * if the row does not exist in this section
- * @see #removeRow(int)
- * @see #addRowAt(int)
- * @see #appendRow()
- * @see #prependRow()
- */
- public void removeRow(ROWTYPE row) {
- try {
- removeRow(rows.indexOf(row));
- } catch (IndexOutOfBoundsException e) {
- throw new IllegalArgumentException(
- "Section does not contain the given row");
- }
- }
-
- /**
- * Gets row at given index.
- *
- * @param rowIndex
- * 0 based index for row. Counted from top to bottom
- * @return row at given index
- */
- public ROWTYPE getRow(int rowIndex) {
- if (rowIndex >= rows.size() || rowIndex < 0) {
- throw new IllegalArgumentException(
- "No row at given index " + rowIndex);
- }
- return rows.get(rowIndex);
- }
-
- /**
- * Adds a new row at the top of this section.
- *
- * @return the new row
- * @see #appendRow()
- * @see #addRowAt(int)
- * @see #removeRow(StaticRow)
- * @see #removeRow(int)
- */
- public ROWTYPE prependRow() {
- return addRowAt(0);
- }
-
- /**
- * Adds a new row at the bottom of this section.
- *
- * @return the new row
- * @see #prependRow()
- * @see #addRowAt(int)
- * @see #removeRow(StaticRow)
- * @see #removeRow(int)
- */
- public ROWTYPE appendRow() {
- return addRowAt(rows.size());
- }
-
- /**
- * Inserts a new row at the given position.
- *
- * @param index
- * the position at which to insert the row
- * @return the new row
- *
- * @throws IndexOutOfBoundsException
- * if the index is out of bounds
- * @see #appendRow()
- * @see #prependRow()
- * @see #removeRow(StaticRow)
- * @see #removeRow(int)
- */
- public ROWTYPE addRowAt(int index) {
- if (index > rows.size() || index < 0) {
- throw new IllegalArgumentException(
- "Unable to add row at index " + index);
- }
- ROWTYPE row = createRow();
- rows.add(index, row);
- getSectionState().rows.add(index, row.getRowState());
-
- for (Object id : grid.columns.keySet()) {
- row.addCell(id);
- }
-
- markAsDirty();
- return row;
- }
-
- /**
- * Gets the amount of rows in this section.
- *
- * @return row count
- */
- public int getRowCount() {
- return rows.size();
- }
-
- protected abstract GridStaticSectionState getSectionState();
-
- protected abstract ROWTYPE createRow();
-
- /**
- * Informs the grid that state has changed and it should be redrawn.
- */
- protected void markAsDirty() {
- grid.markAsDirty();
- }
-
- /**
- * Removes a column for given property id from the section.
- *
- * @param propertyId
- * property to be removed
- */
- protected void removeColumn(Object propertyId) {
- for (ROWTYPE row : rows) {
- row.removeCell(propertyId);
- }
- }
-
- /**
- * Adds a column for given property id to the section.
- *
- * @param propertyId
- * property to be added
- */
- protected void addColumn(Object propertyId) {
- for (ROWTYPE row : rows) {
- row.addCell(propertyId);
- }
- }
-
- /**
- * Performs a sanity check that section is in correct state.
- *
- * @throws IllegalStateException
- * if merged cells are not i n continuous range
- */
- protected void sanityCheck() throws IllegalStateException {
- List<String> columnOrder = grid.getState().columnOrder;
- for (ROWTYPE row : rows) {
- for (Set<String> cellGroup : row.getRowState().cellGroups
- .keySet()) {
- if (!checkCellGroupAndOrder(columnOrder, cellGroup)) {
- throw new IllegalStateException(
- "Not all merged cells were in a continuous range.");
- }
- }
- }
- }
-
- private boolean checkCellGroupAndOrder(List<String> columnOrder,
- Set<String> cellGroup) {
- if (!columnOrder.containsAll(cellGroup)) {
- return false;
- }
-
- for (int i = 0; i < columnOrder.size(); ++i) {
- if (!cellGroup.contains(columnOrder.get(i))) {
- continue;
- }
-
- for (int j = 1; j < cellGroup.size(); ++j) {
- if (!cellGroup.contains(columnOrder.get(i + j))) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
- /**
- * Writes the declarative design to the given table section element.
- *
- * @since 7.5.0
- * @param tableSectionElement
- * Element to write design to
- * @param designContext
- * the design context
- */
- protected void writeDesign(Element tableSectionElement,
- DesignContext designContext) {
- for (ROWTYPE row : rows) {
- row.writeDesign(tableSectionElement.appendElement("tr"),
- designContext);
- }
- }
-
- /**
- * Writes the declarative design from the given table section element.
- *
- * @since 7.5.0
- * @param tableSectionElement
- * Element to read design from
- * @param designContext
- * the design context
- * @throws DesignException
- * if the table section contains unexpected children
- */
- protected void readDesign(Element tableSectionElement,
- DesignContext designContext) throws DesignException {
- while (rows.size() > 0) {
- removeRow(0);
- }
-
- for (Element row : tableSectionElement.children()) {
- if (!row.tagName().equals("tr")) {
- throw new DesignException("Unexpected element in "
- + tableSectionElement.tagName() + ": "
- + row.tagName());
- }
- appendRow().readDesign(row, designContext);
- }
- }
- }
-
- /**
- * Represents the header section of a Grid.
- */
- protected static class Header extends StaticSection<HeaderRow> {
-
- private HeaderRow defaultRow = null;
- private final GridStaticSectionState headerState = new GridStaticSectionState();
-
- protected Header(Grid grid) {
- this.grid = grid;
- grid.getState(true).header = headerState;
- HeaderRow row = createRow();
- rows.add(row);
- setDefaultRow(row);
- getSectionState().rows.add(row.getRowState());
- }
-
- /**
- * Sets the default row of this header. The default row is a special
- * header row providing a user interface for sorting columns.
- *
- * @param row
- * the new default row, or null for no default row
- *
- * @throws IllegalArgumentException
- * this header does not contain the row
- */
- public void setDefaultRow(HeaderRow row) {
- if (row == defaultRow) {
- return;
- }
-
- if (row != null && !rows.contains(row)) {
- throw new IllegalArgumentException(
- "Cannot set a default row that does not exist in the section");
- }
-
- if (defaultRow != null) {
- defaultRow.setDefaultRow(false);
- }
-
- if (row != null) {
- row.setDefaultRow(true);
- }
-
- defaultRow = row;
- markAsDirty();
- }
-
- /**
- * Returns the current default row of this header. The default row is a
- * special header row providing a user interface for sorting columns.
- *
- * @return the default row or null if no default row set
- */
- public HeaderRow getDefaultRow() {
- return defaultRow;
- }
-
- @Override
- protected GridStaticSectionState getSectionState() {
- return headerState;
- }
-
- @Override
- protected HeaderRow createRow() {
- return new HeaderRow(this);
- }
-
- @Override
- public HeaderRow removeRow(int rowIndex) {
- HeaderRow row = super.removeRow(rowIndex);
- if (row == defaultRow) {
- // Default Header Row was just removed.
- setDefaultRow(null);
- }
- return row;
- }
-
- @Override
- protected void sanityCheck() throws IllegalStateException {
- super.sanityCheck();
-
- boolean hasDefaultRow = false;
- for (HeaderRow row : rows) {
- if (row.getRowState().defaultRow) {
- if (!hasDefaultRow) {
- hasDefaultRow = true;
- } else {
- throw new IllegalStateException(
- "Multiple default rows in header");
- }
- }
- }
- }
-
- @Override
- protected void readDesign(Element tableSectionElement,
- DesignContext designContext) {
- super.readDesign(tableSectionElement, designContext);
-
- if (defaultRow == null && !rows.isEmpty()) {
- grid.setDefaultHeaderRow(rows.get(0));
- }
- }
- }
-
- /**
- * Represents a header row in Grid.
- */
- public static class HeaderRow extends StaticSection.StaticRow<HeaderCell> {
-
- protected HeaderRow(StaticSection<?> section) {
- super(section);
- }
-
- private void setDefaultRow(boolean value) {
- getRowState().defaultRow = value;
- }
-
- private boolean isDefaultRow() {
- return getRowState().defaultRow;
- }
-
- @Override
- protected HeaderCell createCell() {
- return new HeaderCell(this);
- }
-
- @Override
- protected String getCellTagName() {
- return "th";
- }
-
- @Override
- protected void writeDesign(Element trElement,
- DesignContext designContext) {
- super.writeDesign(trElement, designContext);
-
- if (section.grid.getDefaultHeaderRow() == this) {
- DesignAttributeHandler.writeAttribute("default",
- trElement.attributes(), true, null, boolean.class);
- }
- }
-
- @Override
- protected void readDesign(Element trElement,
- DesignContext designContext) {
- super.readDesign(trElement, designContext);
-
- boolean defaultRow = DesignAttributeHandler.readAttribute("default",
- trElement.attributes(), false, boolean.class);
- if (defaultRow) {
- section.grid.setDefaultHeaderRow(this);
- }
- }
- }
-
- /**
- * Represents a header cell in Grid. Can be a merged cell for multiple
- * columns.
- */
- public static class HeaderCell extends StaticSection.StaticCell {
-
- protected HeaderCell(HeaderRow row) {
- super(row);
- }
- }
-
- /**
- * Represents the footer section of a Grid. By default Footer is not
- * visible.
- */
- protected static class Footer extends StaticSection<FooterRow> {
-
- private final GridStaticSectionState footerState = new GridStaticSectionState();
-
- protected Footer(Grid grid) {
- this.grid = grid;
- grid.getState(true).footer = footerState;
- }
-
- @Override
- protected GridStaticSectionState getSectionState() {
- return footerState;
- }
-
- @Override
- protected FooterRow createRow() {
- return new FooterRow(this);
- }
-
- @Override
- protected void sanityCheck() throws IllegalStateException {
- super.sanityCheck();
- }
- }
-
- /**
- * Represents a footer row in Grid.
- */
- public static class FooterRow extends StaticSection.StaticRow<FooterCell> {
-
- protected FooterRow(StaticSection<?> section) {
- super(section);
- }
-
- @Override
- protected FooterCell createCell() {
- return new FooterCell(this);
- }
-
- @Override
- protected String getCellTagName() {
- return "td";
- }
-
- }
-
- /**
- * Represents a footer cell in Grid.
- */
- public static class FooterCell extends StaticSection.StaticCell {
-
- protected FooterCell(FooterRow row) {
- super(row);
- }
- }
-
- /**
- * A column in the grid. Can be obtained by calling
- * {@link Grid#getColumn(Object propertyId)}.
- */
- public static class Column implements Serializable {
-
- /**
- * The state of the column shared to the client
- */
- private final GridColumnState state;
-
- /**
- * The grid this column is associated with
- */
- private final Grid grid;
-
- /**
- * Backing property for column
- */
- private final Object propertyId;
-
- private Converter<?, Object> converter;
-
- /**
- * A check for allowing the
- * {@link #Column(Grid, GridColumnState, Object) constructor} to call
- * {@link #setConverter(Converter)} with a <code>null</code>, even if
- * model and renderer aren't compatible.
- */
- private boolean isFirstConverterAssignment = true;
-
- /**
- * Internally used constructor.
- *
- * @param grid
- * The grid this column belongs to. Should not be null.
- * @param state
- * the shared state of this column
- * @param propertyId
- * the backing property id for this column
- */
- Column(Grid grid, GridColumnState state, Object propertyId) {
- this.grid = grid;
- this.state = state;
- this.propertyId = propertyId;
- internalSetRenderer(new TextRenderer());
- }
-
- /**
- * Returns the serializable state of this column that is sent to the
- * client side connector.
- *
- * @return the internal state of the column
- */
- GridColumnState getState() {
- return state;
- }
-
- /**
- * Returns the property id for the backing property of this Column
- *
- * @return property id
- */
- public Object getPropertyId() {
- return propertyId;
- }
-
- /**
- * Returns the caption of the header. By default the header caption is
- * the property id of the column.
- *
- * @return the text in the default row of header.
- *
- * @throws IllegalStateException
- * if the column no longer is attached to the grid
- */
- public String getHeaderCaption() throws IllegalStateException {
- checkColumnIsAttached();
-
- return state.headerCaption;
- }
-
- /**
- * Sets the caption of the header. This caption is also used as the
- * hiding toggle caption, unless it is explicitly set via
- * {@link #setHidingToggleCaption(String)}.
- *
- * @param caption
- * the text to show in the caption
- * @return the column itself
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public Column setHeaderCaption(String caption)
- throws IllegalStateException {
- checkColumnIsAttached();
- if (caption == null) {
- caption = ""; // Render null as empty
- }
- caption = Jsoup.parse(caption).text();
- state.headerCaption = caption;
-
- HeaderRow row = grid.getHeader().getDefaultRow();
- if (row != null) {
- row.getCell(grid.getPropertyIdByColumnId(state.id))
- .setText(caption);
- }
- return this;
- }
-
- /**
- * Gets the caption of the hiding toggle for this column.
- *
- * @since 7.5.0
- * @see #setHidingToggleCaption(String)
- * @return the caption for the hiding toggle for this column
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public String getHidingToggleCaption() throws IllegalStateException {
- checkColumnIsAttached();
- return state.hidingToggleCaption;
- }
-
- /**
- * Sets the caption of the hiding toggle for this column. Shown in the
- * toggle for this column in the grid's sidebar when the column is
- * {@link #isHidable() hidable}.
- * <p>
- * The default value is <code>null</code>, and in that case the column's
- * {@link #getHeaderCaption() header caption} is used.
- * <p>
- * <em>NOTE:</em> setting this to empty string might cause the hiding
- * toggle to not render correctly.
- *
- * @since 7.5.0
- * @param hidingToggleCaption
- * the text to show in the column hiding toggle
- * @return the column itself
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public Column setHidingToggleCaption(String hidingToggleCaption)
- throws IllegalStateException {
- checkColumnIsAttached();
- state.hidingToggleCaption = hidingToggleCaption;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns the width (in pixels). By default a column is 100px wide.
- *
- * @return the width in pixels of the column
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public double getWidth() throws IllegalStateException {
- checkColumnIsAttached();
- return state.width;
- }
-
- /**
- * Sets the width (in pixels).
- * <p>
- * This overrides any configuration set by any of
- * {@link #setExpandRatio(int)}, {@link #setMinimumWidth(double)} or
- * {@link #setMaximumWidth(double)}.
- *
- * @param pixelWidth
- * the new pixel width of the column
- * @return the column itself
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- * @throws IllegalArgumentException
- * thrown if pixel width is less than zero
- */
- public Column setWidth(double pixelWidth)
- throws IllegalStateException, IllegalArgumentException {
- checkColumnIsAttached();
- if (pixelWidth < 0) {
- throw new IllegalArgumentException(
- "Pixel width should be greated than 0 (in " + toString()
- + ")");
- }
- if (state.width != pixelWidth) {
- state.width = pixelWidth;
- grid.markAsDirty();
- grid.fireColumnResizeEvent(this, false);
- }
- return this;
- }
-
- /**
- * Returns whether this column has an undefined width.
- *
- * @since 7.6
- * @return whether the width is undefined
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public boolean isWidthUndefined() {
- checkColumnIsAttached();
- return state.width < 0;
- }
-
- /**
- * Marks the column width as undefined. An undefined width means the
- * grid is free to resize the column based on the cell contents and
- * available space in the grid.
- *
- * @return the column itself
- */
- public Column setWidthUndefined() {
- checkColumnIsAttached();
- if (!isWidthUndefined()) {
- state.width = -1;
- grid.markAsDirty();
- grid.fireColumnResizeEvent(this, false);
- }
- return this;
- }
-
- /**
- * Checks if column is attached and throws an
- * {@link IllegalStateException} if it is not
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- protected void checkColumnIsAttached() throws IllegalStateException {
- if (grid.getColumnByColumnId(state.id) == null) {
- throw new IllegalStateException("Column no longer exists.");
- }
- }
-
- /**
- * Sets this column as the last frozen column in its grid.
- *
- * @return the column itself
- *
- * @throws IllegalArgumentException
- * if the column is no longer attached to any grid
- * @see Grid#setFrozenColumnCount(int)
- */
- public Column setLastFrozenColumn() {
- checkColumnIsAttached();
- grid.setFrozenColumnCount(
- grid.getState(false).columnOrder.indexOf(getState().id)
- + 1);
- return this;
- }
-
- /**
- * Sets the renderer for this column.
- * <p>
- * If a suitable converter isn't defined explicitly, the session
- * converter factory is used to find a compatible converter.
- *
- * @param renderer
- * the renderer to use
- * @return the column itself
- *
- * @throws IllegalArgumentException
- * if no compatible converter could be found
- *
- * @see VaadinSession#getConverterFactory()
- * @see ConverterUtil#getConverter(Class, Class, VaadinSession)
- * @see #setConverter(Converter)
- */
- public Column setRenderer(Renderer<?> renderer) {
- if (!internalSetRenderer(renderer)) {
- throw new IllegalArgumentException(
- "Could not find a converter for converting from the model type "
- + getModelType()
- + " to the renderer presentation type "
- + renderer.getPresentationType() + " (in "
- + toString() + ")");
- }
- return this;
- }
-
- /**
- * Sets the renderer for this column and the converter used to convert
- * from the property value type to the renderer presentation type.
- *
- * @param renderer
- * the renderer to use, cannot be null
- * @param converter
- * the converter to use
- * @return the column itself
- *
- * @throws IllegalArgumentException
- * if the renderer is already associated with a grid column
- */
- public <T> Column setRenderer(Renderer<T> renderer,
- Converter<? extends T, ?> converter) {
- if (renderer.getParent() != null) {
- throw new IllegalArgumentException(
- "Cannot set a renderer that is already connected to a grid column (in "
- + toString() + ")");
- }
-
- if (getRenderer() != null) {
- grid.removeExtension(getRenderer());
- }
-
- grid.addRenderer(renderer);
- state.rendererConnector = renderer;
- setConverter(converter);
- return this;
- }
-
- /**
- * Sets the converter used to convert from the property value type to
- * the renderer presentation type.
- *
- * @param converter
- * the converter to use, or {@code null} to not use any
- * converters
- * @return the column itself
- *
- * @throws IllegalArgumentException
- * if the types are not compatible
- */
- public Column setConverter(Converter<?, ?> converter)
- throws IllegalArgumentException {
- Class<?> modelType = getModelType();
- if (converter != null) {
- if (!converter.getModelType().isAssignableFrom(modelType)) {
- throw new IllegalArgumentException(
- "The converter model type "
- + converter.getModelType()
- + " is not compatible with the property type "
- + modelType + " (in " + toString() + ")");
-
- } else if (!getRenderer().getPresentationType()
- .isAssignableFrom(converter.getPresentationType())) {
- throw new IllegalArgumentException(
- "The converter presentation type "
- + converter.getPresentationType()
- + " is not compatible with the renderer presentation type "
- + getRenderer().getPresentationType()
- + " (in " + toString() + ")");
- }
- }
-
- else {
- /*
- * Since the converter is null (i.e. will be removed), we need
- * to know that the renderer and model are compatible. If not,
- * we can't allow for this to happen.
- *
- * The constructor is allowed to call this method with null
- * without any compatibility checks, therefore we have a special
- * case for it.
- */
-
- Class<?> rendererPresentationType = getRenderer()
- .getPresentationType();
- if (!isFirstConverterAssignment && !rendererPresentationType
- .isAssignableFrom(modelType)) {
- throw new IllegalArgumentException(
- "Cannot remove converter, "
- + "as renderer's presentation type "
- + rendererPresentationType.getName()
- + " and column's " + "model "
- + modelType.getName() + " type aren't "
- + "directly compatible with each other (in "
- + toString() + ")");
- }
- }
-
- isFirstConverterAssignment = false;
-
- @SuppressWarnings("unchecked")
- Converter<?, Object> castConverter = (Converter<?, Object>) converter;
- this.converter = castConverter;
-
- return this;
- }
-
- /**
- * Returns the renderer instance used by this column.
- *
- * @return the renderer
- */
- public Renderer<?> getRenderer() {
- return (Renderer<?>) getState().rendererConnector;
- }
-
- /**
- * Returns the converter instance used by this column.
- *
- * @return the converter
- */
- public Converter<?, ?> getConverter() {
- return converter;
- }
-
- private <T> boolean internalSetRenderer(Renderer<T> renderer) {
-
- Converter<? extends T, ?> converter;
- if (isCompatibleWithProperty(renderer, getConverter())) {
- // Use the existing converter (possibly none) if types
- // compatible
- converter = (Converter<? extends T, ?>) getConverter();
- } else {
- converter = ConverterUtil.getConverter(
- renderer.getPresentationType(), getModelType(),
- getSession());
- }
- setRenderer(renderer, converter);
- return isCompatibleWithProperty(renderer, converter);
- }
-
- private VaadinSession getSession() {
- UI ui = grid.getUI();
- return ui != null ? ui.getSession() : null;
- }
-
- private boolean isCompatibleWithProperty(Renderer<?> renderer,
- Converter<?, ?> converter) {
- Class<?> type;
- if (converter == null) {
- type = getModelType();
- } else {
- type = converter.getPresentationType();
- }
- return renderer.getPresentationType().isAssignableFrom(type);
- }
-
- private Class<?> getModelType() {
- return grid.getContainerDataSource()
- .getType(grid.getPropertyIdByColumnId(state.id));
- }
-
- /**
- * Sets whether this column is sortable by the user. The grid can be
- * sorted by a sortable column by clicking or tapping the column's
- * default header. Programmatic sorting using the Grid#sort methods is
- * not affected by this setting.
- *
- * @param sortable
- * {@code true} if the user should be able to sort the
- * column, {@code false} otherwise
- * @return the column itself
- *
- * @throws IllegalStateException
- * if the data source of the Grid does not implement
- * {@link Sortable}
- * @throws IllegalStateException
- * if the data source does not support sorting by the
- * property associated with this column
- */
- public Column setSortable(boolean sortable) {
- checkColumnIsAttached();
-
- if (sortable) {
- if (!(grid.datasource instanceof Sortable)) {
- throw new IllegalStateException("Can't set column "
- + toString()
- + " sortable. The Container of Grid does not implement Sortable");
- } else if (!((Sortable) grid.datasource)
- .getSortableContainerPropertyIds()
- .contains(propertyId)) {
- throw new IllegalStateException(
- "Can't set column " + toString()
- + " sortable. Container doesn't support sorting by property "
- + propertyId);
- }
- }
-
- state.sortable = sortable;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns whether the user can sort the grid by this column.
- * <p>
- * <em>Note:</em> it is possible to sort by this column programmatically
- * using the Grid#sort methods regardless of the returned value.
- *
- * @return {@code true} if the column is sortable by the user,
- * {@code false} otherwise
- */
- public boolean isSortable() {
- return state.sortable;
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[propertyId:"
- + grid.getPropertyIdByColumnId(state.id) + "]";
- }
-
- /**
- * Sets the ratio with which the column expands.
- * <p>
- * By default, all columns expand equally (treated as if all of them had
- * an expand ratio of 1). Once at least one column gets a defined expand
- * ratio, the implicit expand ratio is removed, and only the defined
- * expand ratios are taken into account.
- * <p>
- * If a column has a defined width ({@link #setWidth(double)}), it
- * overrides this method's effects.
- * <p>
- * <em>Example:</em> A grid with three columns, with expand ratios 0, 1
- * and 2, respectively. The column with a <strong>ratio of 0 is exactly
- * as wide as its contents requires</strong>. The column with a ratio of
- * 1 is as wide as it needs, <strong>plus a third of any excess
- * space</strong>, because we have 3 parts total, and this column
- * reserves only one of those. The column with a ratio of 2, is as wide
- * as it needs to be, <strong>plus two thirds</strong> of the excess
- * width.
- *
- * @param expandRatio
- * the expand ratio of this column. {@code 0} to not have it
- * expand at all. A negative number to clear the expand
- * value.
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- * @see #setWidth(double)
- */
- public Column setExpandRatio(int expandRatio)
- throws IllegalStateException {
- checkColumnIsAttached();
-
- getState().expandRatio = expandRatio;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns the column's expand ratio.
- *
- * @return the column's expand ratio
- * @see #setExpandRatio(int)
- */
- public int getExpandRatio() {
- return getState().expandRatio;
- }
-
- /**
- * Clears the expand ratio for this column.
- * <p>
- * Equal to calling {@link #setExpandRatio(int) setExpandRatio(-1)}
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- */
- public Column clearExpandRatio() throws IllegalStateException {
- return setExpandRatio(-1);
- }
-
- /**
- * Sets the minimum width for this column.
- * <p>
- * This defines the minimum guaranteed pixel width of the column
- * <em>when it is set to expand</em>.
- *
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- * @see #setExpandRatio(int)
- */
- public Column setMinimumWidth(double pixels)
- throws IllegalStateException {
- checkColumnIsAttached();
-
- final double maxwidth = getMaximumWidth();
- if (pixels >= 0 && pixels > maxwidth && maxwidth >= 0) {
- throw new IllegalArgumentException("New minimum width ("
- + pixels + ") was greater than maximum width ("
- + maxwidth + ")");
- }
- getState().minWidth = pixels;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Return the minimum width for this column.
- *
- * @return the minimum width for this column
- * @see #setMinimumWidth(double)
- */
- public double getMinimumWidth() {
- return getState().minWidth;
- }
-
- /**
- * Sets the maximum width for this column.
- * <p>
- * This defines the maximum allowed pixel width of the column <em>when
- * it is set to expand</em>.
- *
- * @param pixels
- * the maximum width
- * @throws IllegalStateException
- * if the column is no longer attached to any grid
- * @see #setExpandRatio(int)
- */
- public Column setMaximumWidth(double pixels) {
- checkColumnIsAttached();
-
- final double minwidth = getMinimumWidth();
- if (pixels >= 0 && pixels < minwidth && minwidth >= 0) {
- throw new IllegalArgumentException("New maximum width ("
- + pixels + ") was less than minimum width (" + minwidth
- + ")");
- }
-
- getState().maxWidth = pixels;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns the maximum width for this column.
- *
- * @return the maximum width for this column
- * @see #setMaximumWidth(double)
- */
- public double getMaximumWidth() {
- return getState().maxWidth;
- }
-
- /**
- * Sets whether the properties corresponding to this column should be
- * editable when the item editor is active. By default columns are
- * editable.
- * <p>
- * Values in non-editable columns are currently not displayed when the
- * editor is active, but this will probably change in the future. They
- * are not automatically assigned an editor field and, if one is
- * manually assigned, it is not used. Columns that cannot (or should
- * not) be edited even in principle should be set non-editable.
- *
- * @param editable
- * {@code true} if this column should be editable,
- * {@code false} otherwise
- * @return this column
- *
- * @throws IllegalStateException
- * if the editor is currently active
- *
- * @see Grid#editItem(Object)
- * @see Grid#isEditorActive()
- */
- public Column setEditable(boolean editable) {
- checkColumnIsAttached();
- if (grid.isEditorActive()) {
- throw new IllegalStateException(
- "Cannot change column editable status while the editor is active");
- }
- getState().editable = editable;
- grid.markAsDirty();
- return this;
- }
-
- /**
- * Returns whether the properties corresponding to this column should be
- * editable when the item editor is active.
- *
- * @return {@code true} if this column is editable, {@code false}
- * otherwise
- *
- * @see Grid#editItem(Object)
- * @see #setEditable(boolean)
- */
-
- public boolean isEditable() {
- return getState().editable;
- }
-
- /**
- * Sets the field component used to edit the properties in this column
- * when the item editor is active. If an item has not been set, then the
- * binding is postponed until the item is set using
- * {@link #editItem(Object)}.
- * <p>
- * Setting the field to <code>null</code> clears any previously set
- * field, causing a new field to be created the next time the item
- * editor is opened.
- *
- * @param editor
- * the editor field
- * @return this column
- */
- public Column setEditorField(Field<?> editor) {
- grid.setEditorField(getPropertyId(), editor);
- return this;
- }
-
- /**
- * Returns the editor field used to edit the properties in this column
- * when the item editor is active. Returns null if the column is not
- * {@link Column#isEditable() editable}.
- * <p>
- * When {@link #editItem(Object) editItem} is called, fields are
- * automatically created and bound for any unbound properties.
- * <p>
- * Getting a field before the editor has been opened depends on special
- * support from the {@link FieldGroup} in use. Using this method with a
- * user-provided <code>FieldGroup</code> might cause
- * {@link FieldGroup.BindException BindException} to be thrown.
- *
- * @return the bound field; or <code>null</code> if the respective
- * column is not editable
- *
- * @throws IllegalArgumentException
- * if there is no column for the provided property id
- * @throws FieldGroup.BindException
- * if no field has been configured and there is a problem
- * building or binding
- */
- public Field<?> getEditorField() {
- return grid.getEditorField(getPropertyId());
- }
-
- /**
- * Hides or shows the column. By default columns are visible before
- * explicitly hiding them.
- *
- * @since 7.5.0
- * @param hidden
- * <code>true</code> to hide the column, <code>false</code>
- * to show
- * @return this column
- */
- public Column setHidden(boolean hidden) {
- if (hidden != getState().hidden) {
- getState().hidden = hidden;
- grid.markAsDirty();
- grid.fireColumnVisibilityChangeEvent(this, hidden, false);
- }
- return this;
- }
-
- /**
- * Returns whether this column is hidden. Default is {@code false}.
- *
- * @since 7.5.0
- * @return <code>true</code> if the column is currently hidden,
- * <code>false</code> otherwise
- */
- public boolean isHidden() {
- return getState().hidden;
- }
-
- /**
- * Sets whether this column can be hidden by the user. Hidable columns
- * can be hidden and shown via the sidebar menu.
- *
- * @since 7.5.0
- * @param hidable
- * <code>true</code> iff the column may be hidable by the
- * user via UI interaction
- * @return this column
- */
- public Column setHidable(boolean hidable) {
- if (hidable != getState().hidable) {
- getState().hidable = hidable;
- grid.markAsDirty();
- }
- return this;
- }
-
- /**
- * Returns whether this column can be hidden by the user. Default is
- * {@code false}.
- * <p>
- * <em>Note:</em> the column can be programmatically hidden using
- * {@link #setHidden(boolean)} regardless of the returned value.
- *
- * @since 7.5.0
- * @return <code>true</code> if the user can hide the column,
- * <code>false</code> if not
- */
- public boolean isHidable() {
- return getState().hidable;
- }
-
- /**
- * Sets whether this column can be resized by the user.
- *
- * @since 7.6
- * @param resizable
- * {@code true} if this column should be resizable,
- * {@code false} otherwise
- */
- public Column setResizable(boolean resizable) {
- if (resizable != getState().resizable) {
- getState().resizable = resizable;
- grid.markAsDirty();
- }
- return this;
- }
-
- /**
- * Returns whether this column can be resized by the user. Default is
- * {@code true}.
- * <p>
- * <em>Note:</em> the column can be programmatically resized using
- * {@link #setWidth(double)} and {@link #setWidthUndefined()} regardless
- * of the returned value.
- *
- * @since 7.6
- * @return {@code true} if this column is resizable, {@code false}
- * otherwise
- */
- public boolean isResizable() {
- return getState().resizable;
- }
-
- /**
- * Writes the design attributes for this column into given element.
- *
- * @since 7.5.0
- *
- * @param design
- * Element to write attributes into
- *
- * @param designContext
- * the design context
- */
- protected void writeDesign(Element design,
- DesignContext designContext) {
- Attributes attributes = design.attributes();
- GridColumnState def = new GridColumnState();
-
- DesignAttributeHandler.writeAttribute("property-id", attributes,
- getPropertyId(), null, Object.class);
-
- // Sortable is a special attribute that depends on the container.
- DesignAttributeHandler.writeAttribute("sortable", attributes,
- isSortable(), null, boolean.class);
- DesignAttributeHandler.writeAttribute("editable", attributes,
- isEditable(), def.editable, boolean.class);
- DesignAttributeHandler.writeAttribute("resizable", attributes,
- isResizable(), def.resizable, boolean.class);
-
- DesignAttributeHandler.writeAttribute("hidable", attributes,
- isHidable(), def.hidable, boolean.class);
- DesignAttributeHandler.writeAttribute("hidden", attributes,
- isHidden(), def.hidden, boolean.class);
- DesignAttributeHandler.writeAttribute("hiding-toggle-caption",
- attributes, getHidingToggleCaption(), null, String.class);
-
- DesignAttributeHandler.writeAttribute("width", attributes,
- getWidth(), def.width, Double.class);
- DesignAttributeHandler.writeAttribute("min-width", attributes,
- getMinimumWidth(), def.minWidth, Double.class);
- DesignAttributeHandler.writeAttribute("max-width", attributes,
- getMaximumWidth(), def.maxWidth, Double.class);
- DesignAttributeHandler.writeAttribute("expand", attributes,
- getExpandRatio(), def.expandRatio, Integer.class);
- }
-
- /**
- * Reads the design attributes for this column from given element.
- *
- * @since 7.5.0
- * @param design
- * Element to read attributes from
- * @param designContext
- * the design context
- */
- protected void readDesign(Element design, DesignContext designContext) {
- Attributes attributes = design.attributes();
-
- if (design.hasAttr("sortable")) {
- setSortable(DesignAttributeHandler.readAttribute("sortable",
- attributes, boolean.class));
- }
- if (design.hasAttr("editable")) {
- setEditable(DesignAttributeHandler.readAttribute("editable",
- attributes, boolean.class));
- }
- if (design.hasAttr("resizable")) {
- setResizable(DesignAttributeHandler.readAttribute("resizable",
- attributes, boolean.class));
- }
-
- if (design.hasAttr("hidable")) {
- setHidable(DesignAttributeHandler.readAttribute("hidable",
- attributes, boolean.class));
- }
- if (design.hasAttr("hidden")) {
- setHidden(DesignAttributeHandler.readAttribute("hidden",
- attributes, boolean.class));
- }
- if (design.hasAttr("hiding-toggle-caption")) {
- setHidingToggleCaption(DesignAttributeHandler.readAttribute(
- "hiding-toggle-caption", attributes, String.class));
- }
-
- // Read size info where necessary.
- if (design.hasAttr("width")) {
- setWidth(DesignAttributeHandler.readAttribute("width",
- attributes, Double.class));
- }
- if (design.hasAttr("min-width")) {
- setMinimumWidth(DesignAttributeHandler
- .readAttribute("min-width", attributes, Double.class));
- }
- if (design.hasAttr("max-width")) {
- setMaximumWidth(DesignAttributeHandler
- .readAttribute("max-width", attributes, Double.class));
- }
- if (design.hasAttr("expand")) {
- if (design.attr("expand").isEmpty()) {
- setExpandRatio(1);
- } else {
- setExpandRatio(DesignAttributeHandler.readAttribute(
- "expand", attributes, Integer.class));
- }
- }
- }
- }
-
- /**
- * An abstract base class for server-side {@link Renderer Grid renderers}.
- * This class currently extends the AbstractExtension superclass, but this
- * fact should be regarded as an implementation detail and subject to change
- * in a future major or minor Vaadin revision.
- *
- * @param <T>
- * the type this renderer knows how to present
- */
- public static abstract class AbstractRenderer<T>
- extends AbstractGridExtension implements Renderer<T> {
-
- private final Class<T> presentationType;
-
- private final String nullRepresentation;
-
- protected AbstractRenderer(Class<T> presentationType,
- String nullRepresentation) {
- this.presentationType = presentationType;
- this.nullRepresentation = nullRepresentation;
- }
-
- protected AbstractRenderer(Class<T> presentationType) {
- this(presentationType, null);
- }
-
- /**
- * This method is inherited from AbstractExtension but should never be
- * called directly with an AbstractRenderer.
- */
- @Deprecated
- @Override
- protected Class<Grid> getSupportedParentType() {
- return Grid.class;
- }
-
- /**
- * This method is inherited from AbstractExtension but should never be
- * called directly with an AbstractRenderer.
- */
- @Deprecated
- @Override
- protected void extend(AbstractClientConnector target) {
- super.extend(target);
- }
-
- @Override
- public Class<T> getPresentationType() {
- return presentationType;
- }
-
- @Override
- public JsonValue encode(T value) {
- if (value == null) {
- return encode(getNullRepresentation(), String.class);
- } else {
- return encode(value, getPresentationType());
- }
- }
-
- /**
- * Null representation for the renderer
- *
- * @return a textual representation of {@code null}
- */
- protected String getNullRepresentation() {
- return nullRepresentation;
- }
-
- /**
- * Encodes the given value to JSON.
- * <p>
- * This is a helper method that can be invoked by an
- * {@link #encode(Object) encode(T)} override if serializing a value of
- * type other than {@link #getPresentationType() the presentation type}
- * is desired. For instance, a {@code Renderer<Date>} could first turn a
- * date value into a formatted string and return
- * {@code encode(dateString, String.class)}.
- *
- * @param value
- * the value to be encoded
- * @param type
- * the type of the value
- * @return a JSON representation of the given value
- */
- protected <U> JsonValue encode(U value, Class<U> type) {
- return JsonCodec
- .encode(value, null, type, getUI().getConnectorTracker())
- .getEncodedValue();
- }
-
- /**
- * Converts and encodes the given data model property value using the
- * given converter and renderer. This method is public only for testing
- * purposes.
- *
- * @since 7.6
- * @param renderer
- * the renderer to use
- * @param converter
- * the converter to use
- * @param modelValue
- * the value to convert and encode
- * @param locale
- * the locale to use in conversion
- * @return an encoded value ready to be sent to the client
- */
- public static <T> JsonValue encodeValue(Object modelValue,
- Renderer<T> renderer, Converter<?, ?> converter,
- Locale locale) {
- Class<T> presentationType = renderer.getPresentationType();
- T presentationValue;
-
- if (converter == null) {
- try {
- presentationValue = presentationType.cast(modelValue);
- } catch (ClassCastException e) {
- if (presentationType == String.class) {
- // If there is no converter, just fallback to using
- // toString(). modelValue can't be null as
- // Class.cast(null) will always succeed
- presentationValue = (T) modelValue.toString();
- } else {
- throw new Converter.ConversionException(
- "Unable to convert value of type "
- + modelValue.getClass().getName()
- + " to presentation type "
- + presentationType.getName()
- + ". No converter is set and the types are not compatible.");
- }
- }
- } else {
- assert presentationType
- .isAssignableFrom(converter.getPresentationType());
- @SuppressWarnings("unchecked")
- Converter<T, Object> safeConverter = (Converter<T, Object>) converter;
- presentationValue = safeConverter.convertToPresentation(
- modelValue, safeConverter.getPresentationType(),
- locale);
- }
-
- JsonValue encodedValue;
- try {
- encodedValue = renderer.encode(presentationValue);
- } catch (Exception e) {
- getLogger().log(Level.SEVERE, "Unable to encode data", e);
- encodedValue = renderer.encode(null);
- }
-
- return encodedValue;
- }
-
- private static Logger getLogger() {
- return Logger.getLogger(AbstractRenderer.class.getName());
- }
-
- }
-
- /**
- * An abstract base class for server-side Grid extensions.
- * <p>
- * Note: If the extension is an instance of {@link DataGenerator} it will
- * automatically register itself to {@link RpcDataProviderExtension} of
- * extended Grid. On remove this registration is automatically removed.
- *
- * @since 7.5
- */
- public static abstract class AbstractGridExtension
- extends AbstractExtension {
-
- /**
- * Constructs a new Grid extension.
- */
- public AbstractGridExtension() {
- super();
- }
-
- /**
- * Constructs a new Grid extension and extends given Grid.
- *
- * @param grid
- * a grid instance
- */
- public AbstractGridExtension(Grid grid) {
- super();
- extend(grid);
- }
-
- @Override
- protected void extend(AbstractClientConnector target) {
- super.extend(target);
-
- if (this instanceof DataGenerator) {
- getParentGrid().datasourceExtension
- .addDataGenerator((DataGenerator) this);
- }
- }
-
- @Override
- public void remove() {
- if (this instanceof DataGenerator) {
- getParentGrid().datasourceExtension
- .removeDataGenerator((DataGenerator) this);
- }
-
- super.remove();
- }
-
- /**
- * Gets the item id for a row key.
- * <p>
- * A key is used to identify a particular row on both a server and a
- * client. This method can be used to get the item id for the row key
- * that the client has sent.
- *
- * @param rowKey
- * the row key for which to retrieve an item id
- * @return the item id corresponding to {@code key}
- */
- protected Object getItemId(String rowKey) {
- return getParentGrid().getKeyMapper().get(rowKey);
- }
-
- /**
- * Gets the column for a column id.
- * <p>
- * An id is used to identify a particular column on both a server and a
- * client. This method can be used to get the column for the column id
- * that the client has sent.
- *
- * @param columnId
- * the column id for which to retrieve a column
- * @return the column corresponding to {@code columnId}
- */
- protected Column getColumn(String columnId) {
- return getParentGrid().getColumnByColumnId(columnId);
- }
-
- /**
- * Gets the parent Grid of the renderer.
- *
- * @return parent grid
- * @throws IllegalStateException
- * if parent is not Grid
- */
- protected Grid getParentGrid() {
- if (getParent() instanceof Grid) {
- Grid grid = (Grid) getParent();
- return grid;
- } else if (getParent() == null) {
- throw new IllegalStateException(
- "Renderer is not attached to any parent");
- } else {
- throw new IllegalStateException(
- "Renderers can be used only with Grid. Extended "
- + getParent().getClass().getSimpleName()
- + " instead");
- }
- }
-
- /**
- * Resends the row data for given item id to the client.
- *
- * @since 7.6
- * @param itemId
- * row to refresh
- */
- protected void refreshRow(Object itemId) {
- getParentGrid().datasourceExtension.updateRowData(itemId);
- }
-
- /**
- * Informs the parent Grid that this Extension wants to add a child
- * component to it.
- *
- * @since 7.6
- * @param c
- * component
- */
- protected void addComponentToGrid(Component c) {
- getParentGrid().addComponent(c);
- }
-
- /**
- * Informs the parent Grid that this Extension wants to remove a child
- * component from it.
- *
- * @since 7.6
- * @param c
- * component
- */
- protected void removeComponentFromGrid(Component c) {
- getParentGrid().removeComponent(c);
- }
- }
-
- /**
- * The data source attached to the grid
- */
- private Container.Indexed datasource;
-
- /**
- * Property id to column instance mapping
- */
- private final Map<Object, Column> columns = new HashMap<Object, Column>();
-
- /**
- * Key generator for column server-to-client communication
- */
- private final KeyMapper<Object> columnKeys = new KeyMapper<Object>();
-
- /**
- * The current sort order
- */
- private final List<SortOrder> sortOrder = new ArrayList<SortOrder>();
-
- /**
- * Property listener for listening to changes in data source properties.
- */
- private final PropertySetChangeListener propertyListener = new PropertySetChangeListener() {
-
- @Override
- public void containerPropertySetChange(PropertySetChangeEvent event) {
- Collection<?> properties = new HashSet<Object>(
- event.getContainer().getContainerPropertyIds());
-
- // Find columns that need to be removed.
- List<Column> removedColumns = new LinkedList<Column>();
- for (Object propertyId : columns.keySet()) {
- if (!properties.contains(propertyId)) {
- removedColumns.add(getColumn(propertyId));
- }
- }
-
- // Actually remove columns.
- for (Column column : removedColumns) {
- Object propertyId = column.getPropertyId();
- internalRemoveColumn(propertyId);
- columnKeys.remove(propertyId);
- }
- datasourceExtension.columnsRemoved(removedColumns);
-
- // Add new columns
- List<Column> addedColumns = new LinkedList<Column>();
- for (Object propertyId : properties) {
- if (!columns.containsKey(propertyId)) {
- addedColumns.add(appendColumn(propertyId));
- }
- }
- datasourceExtension.columnsAdded(addedColumns);
-
- if (getFrozenColumnCount() > columns.size()) {
- setFrozenColumnCount(columns.size());
- }
-
- // Unset sortable for non-sortable columns.
- if (datasource instanceof Sortable) {
- Collection<?> sortables = ((Sortable) datasource)
- .getSortableContainerPropertyIds();
- for (Object propertyId : columns.keySet()) {
- Column column = columns.get(propertyId);
- if (!sortables.contains(propertyId)
- && column.isSortable()) {
- column.setSortable(false);
- }
- }
- }
- }
- };
-
- private final ItemSetChangeListener editorClosingItemSetListener = new ItemSetChangeListener() {
- @Override
- public void containerItemSetChange(ItemSetChangeEvent event) {
- cancelEditor();
- }
- };
-
- private RpcDataProviderExtension datasourceExtension;
-
- /**
- * The selection model that is currently in use. Never <code>null</code>
- * after the constructor has been run.
- */
- private SelectionModel selectionModel;
-
- /**
- * Used to know whether selection change events originate from the server or
- * the client so the selection change handler knows whether the changes
- * should be sent to the client.
- */
- private boolean applyingSelectionFromClient;
-
- private final Header header = new Header(this);
- private final Footer footer = new Footer(this);
-
- private Object editedItemId = null;
- private boolean editorActive = false;
-
- /**
- * True while the editor is storing the field values, i.e. commiting the
- * field group.
- */
- private boolean editorSaving = false;
- private FieldGroup editorFieldGroup = new CustomFieldGroup();
-
- /**
- * Poperty ID to Field mapping that stores editor fields set by
- * {@link #setEditorField(Object, Field)}.
- */
- private Map<Object, Field<?>> editorFields = new HashMap<Object, Field<?>>();
-
- private CellStyleGenerator cellStyleGenerator;
- private RowStyleGenerator rowStyleGenerator;
-
- private CellDescriptionGenerator cellDescriptionGenerator;
- private RowDescriptionGenerator rowDescriptionGenerator;
-
- /**
- * <code>true</code> if Grid is using the internal IndexedContainer created
- * in Grid() constructor, or <code>false</code> if the user has set their
- * own Container.
- *
- * @see #setContainerDataSource(Indexed)
- * @see #Grid()
- */
- private boolean defaultContainer = true;
-
- private EditorErrorHandler editorErrorHandler = new DefaultEditorErrorHandler();
-
- private DetailComponentManager detailComponentManager = null;
-
- private Set<Component> extensionComponents = new HashSet<Component>();
-
- private static final Method SELECTION_CHANGE_METHOD = ReflectTools
- .findMethod(SelectionListener.class, "select",
- SelectionEvent.class);
-
- private static final Method SORT_ORDER_CHANGE_METHOD = ReflectTools
- .findMethod(SortListener.class, "sort", SortEvent.class);
-
- private static final Method COLUMN_REORDER_METHOD = ReflectTools.findMethod(
- ColumnReorderListener.class, "columnReorder",
- ColumnReorderEvent.class);
-
- private static final Method COLUMN_RESIZE_METHOD = ReflectTools.findMethod(
- ColumnResizeListener.class, "columnResize",
- ColumnResizeEvent.class);
-
- private static final Method COLUMN_VISIBILITY_METHOD = ReflectTools
- .findMethod(ColumnVisibilityChangeListener.class,
- "columnVisibilityChanged",
- ColumnVisibilityChangeEvent.class);
-
- /**
- * Creates a new Grid with a new {@link IndexedContainer} as the data
- * source.
- */
- public Grid() {
- this(null, null);
- }
-
- /**
- * Creates a new Grid using the given data source.
- *
- * @param dataSource
- * the indexed container to use as a data source
- */
- public Grid(final Container.Indexed dataSource) {
- this(null, dataSource);
- }
-
- /**
- * Creates a new Grid with the given caption and a new
- * {@link IndexedContainer} data source.
- *
- * @param caption
- * the caption of the grid
- */
- public Grid(String caption) {
- this(caption, null);
- }
-
- /**
- * Creates a new Grid with the given caption and data source. If the data
- * source is null, a new {@link IndexedContainer} will be used.
- *
- * @param caption
- * the caption of the grid
- * @param dataSource
- * the indexed container to use as a data source
- */
- public Grid(String caption, Container.Indexed dataSource) {
- if (dataSource == null) {
- internalSetContainerDataSource(new IndexedContainer());
- } else {
- setContainerDataSource(dataSource);
- }
- setCaption(caption);
- initGrid();
- }
-
- /**
- * Grid initial setup
- */
- private void initGrid() {
- setSelectionMode(getDefaultSelectionMode());
-
- registerRpc(new GridServerRpc() {
-
- @Override
- public void sort(String[] columnIds, SortDirection[] directions,
- boolean userOriginated) {
- assert columnIds.length == directions.length;
-
- List<SortOrder> order = new ArrayList<SortOrder>(
- columnIds.length);
- for (int i = 0; i < columnIds.length; i++) {
- Object propertyId = getPropertyIdByColumnId(columnIds[i]);
- order.add(new SortOrder(propertyId, directions[i]));
- }
- setSortOrder(order, userOriginated);
- if (!order.equals(getSortOrder())) {
- /*
- * Actual sort order is not what the client expects. Make
- * sure the client gets a state change event by clearing the
- * diffstate and marking as dirty
- */
- ConnectorTracker connectorTracker = getUI()
- .getConnectorTracker();
- JsonObject diffState = connectorTracker
- .getDiffState(Grid.this);
- diffState.remove("sortColumns");
- diffState.remove("sortDirs");
- markAsDirty();
- }
- }
-
- @Override
- public void itemClick(String rowKey, String columnId,
- MouseEventDetails details) {
- Object itemId = getKeyMapper().get(rowKey);
- Item item = datasource.getItem(itemId);
- Object propertyId = getPropertyIdByColumnId(columnId);
- fireEvent(new ItemClickEvent(Grid.this, item, itemId,
- propertyId, details));
- }
-
- @Override
- public void columnsReordered(List<String> newColumnOrder,
- List<String> oldColumnOrder) {
- final String diffStateKey = "columnOrder";
- ConnectorTracker connectorTracker = getUI()
- .getConnectorTracker();
- JsonObject diffState = connectorTracker.getDiffState(Grid.this);
- // discard the change if the columns have been reordered from
- // the server side, as the server side is always right
- if (getState(false).columnOrder.equals(oldColumnOrder)) {
- // Don't mark as dirty since client has the state already
- getState(false).columnOrder = newColumnOrder;
- // write changes to diffState so that possible reverting the
- // column order is sent to client
- assert diffState
- .hasKey(diffStateKey) : "Field name has changed";
- Type type = null;
- try {
- type = (getState(false).getClass()
- .getField(diffStateKey).getGenericType());
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- }
- EncodeResult encodeResult = JsonCodec.encode(
- getState(false).columnOrder, diffState, type,
- connectorTracker);
-
- diffState.put(diffStateKey, encodeResult.getEncodedValue());
- fireColumnReorderEvent(true);
- } else {
- // make sure the client is reverted to the order that the
- // server thinks it is
- diffState.remove(diffStateKey);
- markAsDirty();
- }
- }
-
- @Override
- public void columnVisibilityChanged(String id, boolean hidden,
- boolean userOriginated) {
- final Column column = getColumnByColumnId(id);
- final GridColumnState columnState = column.getState();
-
- if (columnState.hidden != hidden) {
- columnState.hidden = hidden;
-
- final String diffStateKey = "columns";
- ConnectorTracker connectorTracker = getUI()
- .getConnectorTracker();
- JsonObject diffState = connectorTracker
- .getDiffState(Grid.this);
-
- assert diffState
- .hasKey(diffStateKey) : "Field name has changed";
- Type type = null;
- try {
- type = (getState(false).getClass()
- .getField(diffStateKey).getGenericType());
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- } catch (SecurityException e) {
- e.printStackTrace();
- }
- EncodeResult encodeResult = JsonCodec.encode(
- getState(false).columns, diffState, type,
- connectorTracker);
-
- diffState.put(diffStateKey, encodeResult.getEncodedValue());
-
- fireColumnVisibilityChangeEvent(column, hidden,
- userOriginated);
- }
- }
-
- @Override
- public void contextClick(int rowIndex, String rowKey,
- String columnId, Section section,
- MouseEventDetails details) {
- Object itemId = null;
- if (rowKey != null) {
- itemId = getKeyMapper().get(rowKey);
- }
- fireEvent(new GridContextClickEvent(Grid.this, details, section,
- rowIndex, itemId, getPropertyIdByColumnId(columnId)));
- }
-
- @Override
- public void columnResized(String id, double pixels) {
- final Column column = getColumnByColumnId(id);
- if (column != null && column.isResizable()) {
- column.getState().width = pixels;
- fireColumnResizeEvent(column, true);
- }
- }
- });
-
- registerRpc(new EditorServerRpc() {
-
- @Override
- public void bind(int rowIndex) {
- try {
- Object id = getContainerDataSource().getIdByIndex(rowIndex);
-
- final boolean opening = editedItemId == null;
-
- final boolean moving = !opening && !editedItemId.equals(id);
-
- final boolean allowMove = !isEditorBuffered()
- && getEditorFieldGroup().isValid();
-
- if (opening || !moving || allowMove) {
- doBind(id);
- } else {
- failBind(null);
- }
- } catch (Exception e) {
- failBind(e);
- }
- }
-
- private void doBind(Object id) {
- editedItemId = id;
- doEditItem();
- getEditorRpc().confirmBind(true);
- }
-
- private void failBind(Exception e) {
- if (e != null) {
- handleError(e);
- }
- getEditorRpc().confirmBind(false);
- }
-
- @Override
- public void cancel(int rowIndex) {
- try {
- // For future proofing even though cannot currently fail
- doCancelEditor();
- } catch (Exception e) {
- handleError(e);
- }
- }
-
- @Override
- public void save(int rowIndex) {
- List<String> errorColumnIds = null;
- String errorMessage = null;
- boolean success = false;
- try {
- saveEditor();
- success = true;
- } catch (CommitException e) {
- try {
- CommitErrorEvent event = new CommitErrorEvent(Grid.this,
- e);
- getEditorErrorHandler().commitError(event);
-
- errorMessage = event.getUserErrorMessage();
-
- errorColumnIds = new ArrayList<String>();
- for (Column column : event.getErrorColumns()) {
- errorColumnIds.add(column.state.id);
- }
- } catch (Exception ee) {
- // A badly written error handler can throw an exception,
- // which would lock up the Grid
- handleError(ee);
- }
- } catch (Exception e) {
- handleError(e);
- }
- getEditorRpc().confirmSave(success, errorMessage,
- errorColumnIds);
- }
-
- private void handleError(Exception e) {
- com.vaadin.server.ErrorEvent.findErrorHandler(Grid.this)
- .error(new ConnectorErrorEvent(Grid.this, e));
- }
- });
- }
-
- @Override
- public void beforeClientResponse(boolean initial) {
- try {
- header.sanityCheck();
- footer.sanityCheck();
- } catch (Exception e) {
- e.printStackTrace();
- setComponentError(new ErrorMessage() {
-
- @Override
- public ErrorLevel getErrorLevel() {
- return ErrorLevel.CRITICAL;
- }
-
- @Override
- public String getFormattedHtmlMessage() {
- return "Incorrectly merged cells";
- }
-
- });
- }
-
- super.beforeClientResponse(initial);
- }
-
- /**
- * Sets the grid data source.
- * <p>
- *
- * <strong>Note</strong> Grid columns are based on properties and try to
- * detect a correct converter for the data type. The columns are not
- * reinitialized automatically if the container is changed, and if the same
- * properties are present after container change, the columns are reused.
- * Properties with same names, but different data types will lead to
- * unpredictable behaviour.
- *
- * @param container
- * The container data source. Cannot be null.
- * @throws IllegalArgumentException
- * if the data source is null
- */
- public void setContainerDataSource(Container.Indexed container) {
- defaultContainer = false;
- internalSetContainerDataSource(container);
- }
-
- private void internalSetContainerDataSource(Container.Indexed container) {
- if (container == null) {
- throw new IllegalArgumentException(
- "Cannot set the datasource to null");
- }
- if (datasource == container) {
- return;
- }
-
- // Remove old listeners
- if (datasource instanceof PropertySetChangeNotifier) {
- ((PropertySetChangeNotifier) datasource)
- .removePropertySetChangeListener(propertyListener);
- }
-
- if (datasourceExtension != null) {
- removeExtension(datasourceExtension);
- }
-
- // Remove old DetailComponentManager
- if (detailComponentManager != null) {
- detailComponentManager.remove();
- }
-
- resetEditor();
-
- datasource = container;
-
- //
- // Adjust sort order
- //
-
- if (container instanceof Container.Sortable) {
-
- // If the container is sortable, go through the current sort order
- // and match each item to the sortable properties of the new
- // container. If the new container does not support an item in the
- // current sort order, that item is removed from the current sort
- // order list.
- Collection<?> sortableProps = ((Container.Sortable) getContainerDataSource())
- .getSortableContainerPropertyIds();
-
- Iterator<SortOrder> i = sortOrder.iterator();
- while (i.hasNext()) {
- if (!sortableProps.contains(i.next().getPropertyId())) {
- i.remove();
- }
- }
-
- sort(false);
- } else {
- // Clear sorting order. Don't sort.
- sortOrder.clear();
- }
-
- datasourceExtension = new RpcDataProviderExtension(container);
- datasourceExtension.extend(this);
- datasourceExtension.addDataGenerator(new RowDataGenerator());
- for (Extension e : getExtensions()) {
- if (e instanceof DataGenerator) {
- datasourceExtension.addDataGenerator((DataGenerator) e);
- }
- }
-
- if (detailComponentManager != null) {
- detailComponentManager = new DetailComponentManager(this,
- detailComponentManager.getDetailsGenerator());
- } else {
- detailComponentManager = new DetailComponentManager(this);
- }
-
- /*
- * selectionModel == null when the invocation comes from the
- * constructor.
- */
- if (selectionModel != null) {
- selectionModel.reset();
- }
-
- // Listen to changes in properties and remove columns if needed
- if (datasource instanceof PropertySetChangeNotifier) {
- ((PropertySetChangeNotifier) datasource)
- .addPropertySetChangeListener(propertyListener);
- }
-
- /*
- * activeRowHandler will be updated by the client-side request that
- * occurs on container change - no need to actively re-insert any
- * ValueChangeListeners at this point.
- */
-
- setFrozenColumnCount(0);
-
- if (columns.isEmpty()) {
- // Add columns
- for (Object propertyId : datasource.getContainerPropertyIds()) {
- Column column = appendColumn(propertyId);
-
- // Initial sorting is defined by container
- if (datasource instanceof Sortable) {
- column.setSortable(((Sortable) datasource)
- .getSortableContainerPropertyIds()
- .contains(propertyId));
- } else {
- column.setSortable(false);
- }
- }
- } else {
- Collection<?> properties = datasource.getContainerPropertyIds();
- for (Object property : columns.keySet()) {
- if (!properties.contains(property)) {
- throw new IllegalStateException(
- "Found at least one column in Grid that does not exist in the given container: "
- + property + " with the header \""
- + getColumn(property).getHeaderCaption()
- + "\". "
- + "Call removeAllColumns() before setContainerDataSource() if you want to reconfigure the columns based on the new container.");
- }
-
- if (!(datasource instanceof Sortable)
- || !((Sortable) datasource)
- .getSortableContainerPropertyIds()
- .contains(property)) {
- columns.get(property).setSortable(false);
- }
- }
- }
- }
-
- /**
- * Returns the grid data source.
- *
- * @return the container data source of the grid
- */
- public Container.Indexed getContainerDataSource() {
- return datasource;
- }
-
- /**
- * Returns a column based on the property id
- *
- * @param propertyId
- * the property id of the column
- * @return the column or <code>null</code> if not found
- */
- public Column getColumn(Object propertyId) {
- return columns.get(propertyId);
- }
-
- /**
- * Returns a copy of currently configures columns in their current visual
- * order in this Grid.
- *
- * @return unmodifiable copy of current columns in visual order
- */
- public List<Column> getColumns() {
- List<Column> columns = new ArrayList<Grid.Column>();
- for (String columnId : getState(false).columnOrder) {
- columns.add(getColumnByColumnId(columnId));
- }
- return Collections.unmodifiableList(columns);
- }
-
- /**
- * Adds a new Column to Grid. Also adds the property to container with data
- * type String, if property for column does not exist in it. Default value
- * for the new property is an empty String.
- * <p>
- * Note that adding a new property is only done for the default container
- * that Grid sets up with the default constructor.
- *
- * @param propertyId
- * the property id of the new column
- * @return the new column
- *
- * @throws IllegalStateException
- * if column for given property already exists in this grid
- */
-
- public Column addColumn(Object propertyId) throws IllegalStateException {
- if (datasource.getContainerPropertyIds().contains(propertyId)
- && !columns.containsKey(propertyId)) {
- appendColumn(propertyId);
- } else if (defaultContainer) {
- addColumnProperty(propertyId, String.class, "");
- } else {
- if (columns.containsKey(propertyId)) {
- throw new IllegalStateException(
- "A column for property id '" + propertyId.toString()
- + "' already exists in this grid");
- } else {
- throw new IllegalStateException(
- "Property id '" + propertyId.toString()
- + "' does not exist in the container");
- }
- }
-
- // Inform the data provider of this new column.
- Column column = getColumn(propertyId);
- List<Column> addedColumns = new ArrayList<Column>();
- addedColumns.add(column);
- datasourceExtension.columnsAdded(addedColumns);
-
- return column;
- }
-
- /**
- * Adds a new Column to Grid. This function makes sure that the property
- * with the given id and data type exists in the container. If property does
- * not exists, it will be created.
- * <p>
- * Default value for the new property is 0 if type is Integer, Double and
- * Float. If type is String, default value is an empty string. For all other
- * types the default value is null.
- * <p>
- * Note that adding a new property is only done for the default container
- * that Grid sets up with the default constructor.
- *
- * @param propertyId
- * the property id of the new column
- * @param type
- * the data type for the new property
- * @return the new column
- *
- * @throws IllegalStateException
- * if column for given property already exists in this grid or
- * property already exists in the container with wrong type
- */
- public Column addColumn(Object propertyId, Class<?> type) {
- addColumnProperty(propertyId, type, null);
- return getColumn(propertyId);
- }
-
- protected void addColumnProperty(Object propertyId, Class<?> type,
- Object defaultValue) throws IllegalStateException {
- if (!defaultContainer) {
- throw new IllegalStateException(
- "Container for this Grid is not a default container from Grid() constructor");
- }
-
- if (!columns.containsKey(propertyId)) {
- if (!datasource.getContainerPropertyIds().contains(propertyId)) {
- datasource.addContainerProperty(propertyId, type, defaultValue);
- } else {
- Property<?> containerProperty = datasource.getContainerProperty(
- datasource.firstItemId(), propertyId);
- if (containerProperty.getType() == type) {
- appendColumn(propertyId);
- } else {
- throw new IllegalStateException(
- "DataSource already has the given property "
- + propertyId + " with a different type");
- }
- }
- } else {
- throw new IllegalStateException(
- "Grid already has a column for property " + propertyId);
- }
- }
-
- /**
- * Removes all columns from this Grid.
- */
- public void removeAllColumns() {
- List<Column> removed = new ArrayList<Column>(columns.values());
- Set<Object> properties = new HashSet<Object>(columns.keySet());
- for (Object propertyId : properties) {
- removeColumn(propertyId);
- }
- datasourceExtension.columnsRemoved(removed);
- }
-
- /**
- * Used internally by the {@link Grid} to get a {@link Column} by
- * referencing its generated state id. Also used by {@link Column} to verify
- * if it has been detached from the {@link Grid}.
- *
- * @param columnId
- * the client id generated for the column when the column is
- * added to the grid
- * @return the column with the id or <code>null</code> if not found
- */
- Column getColumnByColumnId(String columnId) {
- Object propertyId = getPropertyIdByColumnId(columnId);
- return getColumn(propertyId);
- }
-
- /**
- * Used internally by the {@link Grid} to get a property id by referencing
- * the columns generated state id.
- *
- * @param columnId
- * The state id of the column
- * @return The column instance or null if not found
- */
- Object getPropertyIdByColumnId(String columnId) {
- return columnKeys.get(columnId);
- }
-
- /**
- * Returns whether column reordering is allowed. Default value is
- * <code>false</code>.
- *
- * @since 7.5.0
- * @return true if reordering is allowed
- */
- public boolean isColumnReorderingAllowed() {
- return getState(false).columnReorderingAllowed;
- }
-
- /**
- * Sets whether or not column reordering is allowed. Default value is
- * <code>false</code>.
- *
- * @since 7.5.0
- * @param columnReorderingAllowed
- * specifies whether column reordering is allowed
- */
- public void setColumnReorderingAllowed(boolean columnReorderingAllowed) {
- if (isColumnReorderingAllowed() != columnReorderingAllowed) {
- getState().columnReorderingAllowed = columnReorderingAllowed;
- }
- }
-
- @Override
- protected GridState getState() {
- return (GridState) super.getState();
- }
-
- @Override
- protected GridState getState(boolean markAsDirty) {
- return (GridState) super.getState(markAsDirty);
- }
-
- /**
- * Sets the column resize mode to use. The default mode is
- * {@link ColumnResizeMode#ANIMATED}.
- *
- * @param mode
- * a ColumnResizeMode value
- *
- * @since 7.7.5
- */
- public void setColumnResizeMode(ColumnResizeMode mode) {
- getState().columnResizeMode = mode;
- }
-
- /**
- * Returns the current column resize mode. The default mode is
- * {@link ColumnResizeMode#ANIMATED}.
- *
- * @return a ColumnResizeMode value
- *
- * @since 7.7.5
- */
- public ColumnResizeMode getColumnResizeMode() {
- return getState(false).columnResizeMode;
- }
-
- /**
- * Creates a new column based on a property id and appends it as the last
- * column.
- *
- * @param datasourcePropertyId
- * The property id of a property in the datasource
- */
- private Column appendColumn(Object datasourcePropertyId) {
- if (datasourcePropertyId == null) {
- throw new IllegalArgumentException("Property id cannot be null");
- }
- assert datasource.getContainerPropertyIds().contains(
- datasourcePropertyId) : "Datasource should contain the property id";
-
- GridColumnState columnState = new GridColumnState();
- columnState.id = columnKeys.key(datasourcePropertyId);
-
- Column column = new Column(this, columnState, datasourcePropertyId);
- columns.put(datasourcePropertyId, column);
-
- getState().columns.add(columnState);
- getState().columnOrder.add(columnState.id);
- header.addColumn(datasourcePropertyId);
- footer.addColumn(datasourcePropertyId);
-
- String humanFriendlyPropertyId = SharedUtil.propertyIdToHumanFriendly(
- String.valueOf(datasourcePropertyId));
- column.setHeaderCaption(humanFriendlyPropertyId);
-
- if (datasource instanceof Sortable
- && ((Sortable) datasource).getSortableContainerPropertyIds()
- .contains(datasourcePropertyId)) {
- column.setSortable(true);
- }
-
- return column;
- }
-
- /**
- * Removes a column from Grid based on a property id.
- *
- * @param propertyId
- * The property id of column to be removed
- *
- * @throws IllegalArgumentException
- * if there is no column for given property id in this grid
- */
- public void removeColumn(Object propertyId)
- throws IllegalArgumentException {
- if (!columns.keySet().contains(propertyId)) {
- throw new IllegalArgumentException(
- "There is no column for given property id " + propertyId);
- }
-
- List<Column> removed = new ArrayList<Column>();
- removed.add(getColumn(propertyId));
- internalRemoveColumn(propertyId);
- datasourceExtension.columnsRemoved(removed);
- }
-
- private void internalRemoveColumn(Object propertyId) {
- setEditorField(propertyId, null);
- header.removeColumn(propertyId);
- footer.removeColumn(propertyId);
- Column column = columns.remove(propertyId);
- getState().columnOrder.remove(columnKeys.key(propertyId));
- getState().columns.remove(column.getState());
- removeExtension(column.getRenderer());
- }
-
- /**
- * Sets the columns and their order for the grid. Current columns whose
- * property id is not in propertyIds are removed. Similarly, a column is
- * added for any property id in propertyIds that has no corresponding column
- * in this Grid.
- *
- * @since 7.5.0
- *
- * @param propertyIds
- * properties in the desired column order
- */
- public void setColumns(Object... propertyIds) {
- if (SharedUtil.containsDuplicates(propertyIds)) {
- throw new IllegalArgumentException(
- "The propertyIds array contains duplicates: "
- + SharedUtil.getDuplicates(propertyIds));
- }
- Set<?> removePids = new HashSet<Object>(columns.keySet());
- removePids.removeAll(Arrays.asList(propertyIds));
- for (Object removePid : removePids) {
- removeColumn(removePid);
- }
- Set<?> addPids = new HashSet<Object>(Arrays.asList(propertyIds));
- addPids.removeAll(columns.keySet());
- for (Object propertyId : addPids) {
- addColumn(propertyId);
- }
- setColumnOrder(propertyIds);
- }
-
- /**
- * Sets a new column order for the grid. All columns which are not ordered
- * here will remain in the order they were before as the last columns of
- * grid.
- *
- * @param propertyIds
- * properties in the order columns should be
- */
- public void setColumnOrder(Object... propertyIds) {
- if (SharedUtil.containsDuplicates(propertyIds)) {
- throw new IllegalArgumentException(
- "The propertyIds array contains duplicates: "
- + SharedUtil.getDuplicates(propertyIds));
- }
- List<String> columnOrder = new ArrayList<String>();
- for (Object propertyId : propertyIds) {
- if (columns.containsKey(propertyId)) {
- columnOrder.add(columnKeys.key(propertyId));
- } else {
- throw new IllegalArgumentException(
- "Grid does not contain column for property "
- + String.valueOf(propertyId));
- }
- }
-
- List<String> stateColumnOrder = getState().columnOrder;
- if (stateColumnOrder.size() != columnOrder.size()) {
- stateColumnOrder.removeAll(columnOrder);
- columnOrder.addAll(stateColumnOrder);
- }
- getState().columnOrder = columnOrder;
- fireColumnReorderEvent(false);
- }
-
- /**
- * Sets the number of frozen columns in this grid. Setting the count to 0
- * means that no data columns will be frozen, but the built-in selection
- * checkbox column will still be frozen if it's in use. Setting the count to
- * -1 will also disable the selection column.
- * <p>
- * The default value is 0.
- *
- * @param numberOfColumns
- * the number of columns that should be frozen
- *
- * @throws IllegalArgumentException
- * if the column count is < 0 or > the number of visible columns
- */
- public void setFrozenColumnCount(int numberOfColumns) {
- if (numberOfColumns < -1 || numberOfColumns > columns.size()) {
- throw new IllegalArgumentException(
- "count must be between -1 and the current number of columns ("
- + columns.size() + "): " + numberOfColumns);
- }
-
- getState().frozenColumnCount = numberOfColumns;
- }
-
- /**
- * Gets the number of frozen columns in this grid. 0 means that no data
- * columns will be frozen, but the built-in selection checkbox column will
- * still be frozen if it's in use. -1 means that not even the selection
- * column is frozen.
- * <p>
- * <em>NOTE:</em> this count includes {@link Column#isHidden() hidden
- * columns} in the count.
- *
- * @see #setFrozenColumnCount(int)
- *
- * @return the number of frozen columns
- */
- public int getFrozenColumnCount() {
- return getState(false).frozenColumnCount;
- }
-
- /**
- * Scrolls to a certain item, using {@link ScrollDestination#ANY}.
- * <p>
- * If the item has visible details, its size will also be taken into
- * account.
- *
- * @param itemId
- * id of item to scroll to.
- * @throws IllegalArgumentException
- * if the provided id is not recognized by the data source.
- */
- public void scrollTo(Object itemId) throws IllegalArgumentException {
- scrollTo(itemId, ScrollDestination.ANY);
- }
-
- /**
- * Scrolls to a certain item, using user-specified scroll destination.
- * <p>
- * If the item has visible details, its size will also be taken into
- * account.
- *
- * @param itemId
- * id of item to scroll to.
- * @param destination
- * value specifying desired position of scrolled-to row.
- * @throws IllegalArgumentException
- * if the provided id is not recognized by the data source.
- */
- public void scrollTo(Object itemId, ScrollDestination destination)
- throws IllegalArgumentException {
-
- int row = datasource.indexOfId(itemId);
-
- if (row == -1) {
- throw new IllegalArgumentException(
- "Item with specified ID does not exist in data source");
- }
-
- GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class);
- clientRPC.scrollToRow(row, destination);
- }
-
- /**
- * Scrolls to the beginning of the first data row.
- */
- public void scrollToStart() {
- GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class);
- clientRPC.scrollToStart();
- }
-
- /**
- * Scrolls to the end of the last data row.
- */
- public void scrollToEnd() {
- GridClientRpc clientRPC = getRpcProxy(GridClientRpc.class);
- clientRPC.scrollToEnd();
- }
-
- /**
- * Sets the number of rows that should be visible in Grid's body, while
- * {@link #getHeightMode()} is {@link HeightMode#ROW}.
- * <p>
- * If Grid is currently not in {@link HeightMode#ROW}, the given value is
- * remembered, and applied once the mode is applied.
- *
- * @param rows
- * The height in terms of number of rows displayed in Grid's
- * body. If Grid doesn't contain enough rows, white space is
- * displayed instead. If <code>null</code> is given, then Grid's
- * height is undefined
- * @throws IllegalArgumentException
- * if {@code rows} is zero or less
- * @throws IllegalArgumentException
- * if {@code rows} is {@link Double#isInfinite(double) infinite}
- * @throws IllegalArgumentException
- * if {@code rows} is {@link Double#isNaN(double) NaN}
- */
- public void setHeightByRows(double rows) {
- if (rows <= 0.0d) {
- throw new IllegalArgumentException(
- "More than zero rows must be shown.");
- } else if (Double.isInfinite(rows)) {
- throw new IllegalArgumentException(
- "Grid doesn't support infinite heights");
- } else if (Double.isNaN(rows)) {
- throw new IllegalArgumentException("NaN is not a valid row count");
- }
-
- getState().heightByRows = rows;
- }
-
- /**
- * Gets the amount of rows in Grid's body that are shown, while
- * {@link #getHeightMode()} is {@link HeightMode#ROW}.
- *
- * @return the amount of rows that are being shown in Grid's body
- * @see #setHeightByRows(double)
- */
- public double getHeightByRows() {
- return getState(false).heightByRows;
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * <em>Note:</em> This method will change the widget's size in the browser
- * only if {@link #getHeightMode()} returns {@link HeightMode#CSS}.
- *
- * @see #setHeightMode(HeightMode)
- */
- @Override
- public void setHeight(float height, Unit unit) {
- super.setHeight(height, unit);
- }
-
- /**
- * Defines the mode in which the Grid widget's height is calculated.
- * <p>
- * If {@link HeightMode#CSS} is given, Grid will respect the values given
- * via a {@code setHeight}-method, and behave as a traditional Component.
- * <p>
- * If {@link HeightMode#ROW} is given, Grid will make sure that the body
- * will display as many rows as {@link #getHeightByRows()} defines.
- * <em>Note:</em> If headers/footers are inserted or removed, the widget
- * will resize itself to still display the required amount of rows in its
- * body. It also takes the horizontal scrollbar into account.
- *
- * @param heightMode
- * the mode in to which Grid should be set
- */
- public void setHeightMode(HeightMode heightMode) {
- /*
- * This method is a workaround for the fact that Vaadin re-applies
- * widget dimensions (height/width) on each state change event. The
- * original design was to have setHeight and setHeightByRow be equals,
- * and whichever was called the latest was considered in effect.
- *
- * But, because of Vaadin always calling setHeight on the widget, this
- * approach doesn't work.
- */
-
- getState().heightMode = heightMode;
- }
-
- /**
- * Returns the current {@link HeightMode} the Grid is in.
- * <p>
- * Defaults to {@link HeightMode#CSS}.
- *
- * @return the current HeightMode
- */
- public HeightMode getHeightMode() {
- return getState(false).heightMode;
- }
-
- /* Selection related methods: */
-
- /**
- * Takes a new {@link SelectionModel} into use.
- * <p>
- * The SelectionModel that is previously in use will have all its items
- * deselected. If any items were selected, this will fire a
- * {@link SelectionEvent}.
- * <p>
- * If the given SelectionModel is already in use, this method does nothing.
- *
- * @param selectionModel
- * the new SelectionModel to use
- * @throws IllegalArgumentException
- * if {@code selectionModel} is <code>null</code>
- */
- public void setSelectionModel(SelectionModel selectionModel)
- throws IllegalArgumentException {
- if (selectionModel == null) {
- throw new IllegalArgumentException(
- "Selection model may not be null");
- }
-
- if (this.selectionModel != selectionModel) {
- Collection<Object> oldSelection;
- // this.selectionModel is null on init
- if (this.selectionModel != null) {
- oldSelection = this.selectionModel.getSelectedRows();
- this.selectionModel.remove();
- } else {
- oldSelection = Collections.emptyList();
- }
-
- this.selectionModel = selectionModel;
- selectionModel.setGrid(this);
- Collection<Object> newSelection = this.selectionModel
- .getSelectedRows();
-
- if (!SharedUtil.equals(oldSelection, newSelection)) {
- fireSelectionEvent(oldSelection, newSelection);
- }
-
- // selection is included in the row data, so the client needs to be
- // updated
- datasourceExtension.refreshCache();
- }
- }
-
- /**
- * Returns the currently used {@link SelectionModel}.
- *
- * @return the currently used SelectionModel
- */
- public SelectionModel getSelectionModel() {
- return selectionModel;
- }
-
- /**
- * Sets the Grid's selection mode.
- * <p>
- * Grid supports three selection modes: multiselect, single select and no
- * selection, and this is a convenience method for choosing between one of
- * them.
- * <p>
- * Technically, this method is a shortcut that can be used instead of
- * calling {@code setSelectionModel} with a specific SelectionModel
- * instance. Grid comes with three built-in SelectionModel classes, and the
- * {@link SelectionMode} enum represents each of them.
- * <p>
- * Essentially, the two following method calls are equivalent:
- * <p>
- * <code><pre>
- * grid.setSelectionMode(SelectionMode.MULTI);
- * grid.setSelectionModel(new MultiSelectionMode());
- * </pre></code>
- *
- *
- * @param selectionMode
- * the selection mode to switch to
- * @return The {@link SelectionModel} instance that was taken into use
- * @throws IllegalArgumentException
- * if {@code selectionMode} is <code>null</code>
- * @see SelectionModel
- */
- public SelectionModel setSelectionMode(final SelectionMode selectionMode)
- throws IllegalArgumentException {
- if (selectionMode == null) {
- throw new IllegalArgumentException(
- "selection mode may not be null");
- }
- final SelectionModel newSelectionModel = selectionMode.createModel();
- setSelectionModel(newSelectionModel);
- return newSelectionModel;
- }
-
- /**
- * Checks whether an item is selected or not.
- *
- * @param itemId
- * the item id to check for
- * @return <code>true</code> iff the item is selected
- */
- // keep this javadoc in sync with SelectionModel.isSelected
- public boolean isSelected(Object itemId) {
- return selectionModel.isSelected(itemId);
- }
-
- /**
- * Returns a collection of all the currently selected itemIds.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}.
- *
- * @return a collection of all the currently selected itemIds
- */
- // keep this javadoc in sync with SelectionModel.getSelectedRows
- public Collection<Object> getSelectedRows() {
- return getSelectionModel().getSelectedRows();
- }
-
- /**
- * Gets the item id of the currently selected item.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}. Only
- * {@link SelectionModel.Single} is supported.
- *
- * @return the item id of the currently selected item, or <code>null</code>
- * if nothing is selected
- * @throws IllegalStateException
- * if the selection model does not implement
- * {@code SelectionModel.Single}
- */
- // keep this javadoc in sync with SelectionModel.Single.getSelectedRow
- public Object getSelectedRow() throws IllegalStateException {
- if (selectionModel instanceof SelectionModel.Single) {
- return ((SelectionModel.Single) selectionModel).getSelectedRow();
- } else if (selectionModel instanceof SelectionModel.Multi) {
- throw new IllegalStateException("Cannot get unique selected row: "
- + "Grid is in multiselect mode "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else if (selectionModel instanceof SelectionModel.None) {
- throw new IllegalStateException(
- "Cannot get selected row: " + "Grid selection is disabled "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else {
- throw new IllegalStateException("Cannot get selected row: "
- + "Grid selection model does not implement "
- + SelectionModel.Single.class.getName() + " or "
- + SelectionModel.Multi.class.getName()
- + "(the current model is "
- + selectionModel.getClass().getName() + ").");
- }
- }
-
- /**
- * Marks an item as selected.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}. Only
- * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are
- * supported.
- *
- * @param itemId
- * the itemId to mark as selected
- * @return <code>true</code> if the selection state changed,
- * <code>false</code> if the itemId already was selected
- * @throws IllegalArgumentException
- * if the {@code itemId} doesn't exist in the currently active
- * Container
- * @throws IllegalStateException
- * if the selection was illegal. One such reason might be that
- * the implementation already had an item selected, and that
- * needs to be explicitly deselected before re-selecting
- * something.
- * @throws IllegalStateException
- * if the selection model does not implement
- * {@code SelectionModel.Single} or {@code SelectionModel.Multi}
- */
- // keep this javadoc in sync with SelectionModel.Single.select
- public boolean select(Object itemId)
- throws IllegalArgumentException, IllegalStateException {
- if (selectionModel instanceof SelectionModel.Single) {
- return ((SelectionModel.Single) selectionModel).select(itemId);
- } else if (selectionModel instanceof SelectionModel.Multi) {
- return ((SelectionModel.Multi) selectionModel).select(itemId);
- } else if (selectionModel instanceof SelectionModel.None) {
- throw new IllegalStateException("Cannot select row '" + itemId
- + "': Grid selection is disabled "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else {
- throw new IllegalStateException("Cannot select row '" + itemId
- + "': Grid selection model does not implement "
- + SelectionModel.Single.class.getName() + " or "
- + SelectionModel.Multi.class.getName()
- + "(the current model is "
- + selectionModel.getClass().getName() + ").");
- }
- }
-
- /**
- * Marks an item as unselected.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}. Only
- * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are
- * supported.
- *
- * @param itemId
- * the itemId to remove from being selected
- * @return <code>true</code> if the selection state changed,
- * <code>false</code> if the itemId was already selected
- * @throws IllegalArgumentException
- * if the {@code itemId} doesn't exist in the currently active
- * Container
- * @throws IllegalStateException
- * if the deselection was illegal. One such reason might be that
- * the implementation requires one or more items to be selected
- * at all times.
- * @throws IllegalStateException
- * if the selection model does not implement
- * {@code SelectionModel.Single} or {code SelectionModel.Multi}
- */
- // keep this javadoc in sync with SelectionModel.Single.deselect
- public boolean deselect(Object itemId) throws IllegalStateException {
- if (selectionModel instanceof SelectionModel.Single) {
- if (isSelected(itemId)) {
- return ((SelectionModel.Single) selectionModel).select(null);
- }
- return false;
- } else if (selectionModel instanceof SelectionModel.Multi) {
- return ((SelectionModel.Multi) selectionModel).deselect(itemId);
- } else if (selectionModel instanceof SelectionModel.None) {
- throw new IllegalStateException("Cannot deselect row '" + itemId
- + "': Grid selection is disabled "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else {
- throw new IllegalStateException("Cannot deselect row '" + itemId
- + "': Grid selection model does not implement "
- + SelectionModel.Single.class.getName() + " or "
- + SelectionModel.Multi.class.getName()
- + "(the current model is "
- + selectionModel.getClass().getName() + ").");
- }
- }
-
- /**
- * Marks all items as unselected.
- * <p>
- * This method is a shorthand that delegates to the
- * {@link #getSelectionModel() selection model}. Only
- * {@link SelectionModel.Single} and {@link SelectionModel.Multi} are
- * supported.
- *
- * @return <code>true</code> if the selection state changed,
- * <code>false</code> if the itemId was already selected
- * @throws IllegalStateException
- * if the deselection was illegal. One such reason might be that
- * the implementation requires one or more items to be selected
- * at all times.
- * @throws IllegalStateException
- * if the selection model does not implement
- * {@code SelectionModel.Single} or {code SelectionModel.Multi}
- */
- public boolean deselectAll() throws IllegalStateException {
- if (selectionModel instanceof SelectionModel.Single) {
- if (getSelectedRow() != null) {
- return deselect(getSelectedRow());
- }
- return false;
- } else if (selectionModel instanceof SelectionModel.Multi) {
- return ((SelectionModel.Multi) selectionModel).deselectAll();
- } else if (selectionModel instanceof SelectionModel.None) {
- throw new IllegalStateException(
- "Cannot deselect all rows" + ": Grid selection is disabled "
- + "(the current selection model is "
- + selectionModel.getClass().getName() + ").");
- } else {
- throw new IllegalStateException("Cannot deselect all rows:"
- + " Grid selection model does not implement "
- + SelectionModel.Single.class.getName() + " or "
- + SelectionModel.Multi.class.getName()
- + "(the current model is "
- + selectionModel.getClass().getName() + ").");
- }
- }
-
- /**
- * Fires a selection change event.
- * <p>
- * <strong>Note:</strong> This is not a method that should be called by
- * application logic. This method is publicly accessible only so that
- * {@link SelectionModel SelectionModels} would be able to inform Grid of
- * these events.
- *
- * @param newSelection
- * the selection that was added by this event
- * @param oldSelection
- * the selection that was removed by this event
- */
- public void fireSelectionEvent(Collection<Object> oldSelection,
- Collection<Object> newSelection) {
- fireEvent(new SelectionEvent(this, oldSelection, newSelection));
- }
-
- @Override
- public void addSelectionListener(SelectionListener listener) {
- addListener(SelectionEvent.class, listener, SELECTION_CHANGE_METHOD);
- }
-
- @Override
- public void removeSelectionListener(SelectionListener listener) {
- removeListener(SelectionEvent.class, listener, SELECTION_CHANGE_METHOD);
- }
-
- private void fireColumnReorderEvent(boolean userOriginated) {
- fireEvent(new ColumnReorderEvent(this, userOriginated));
- }
-
- /**
- * Registers a new column reorder listener.
- *
- * @since 7.5.0
- * @param listener
- * the listener to register
- */
- public void addColumnReorderListener(ColumnReorderListener listener) {
- addListener(ColumnReorderEvent.class, listener, COLUMN_REORDER_METHOD);
- }
-
- /**
- * Removes a previously registered column reorder listener.
- *
- * @since 7.5.0
- * @param listener
- * the listener to remove
- */
- public void removeColumnReorderListener(ColumnReorderListener listener) {
- removeListener(ColumnReorderEvent.class, listener,
- COLUMN_REORDER_METHOD);
- }
-
- private void fireColumnResizeEvent(Column column, boolean userOriginated) {
- fireEvent(new ColumnResizeEvent(this, column, userOriginated));
- }
-
- /**
- * Registers a new column resize listener.
- *
- * @param listener
- * the listener to register
- */
- public void addColumnResizeListener(ColumnResizeListener listener) {
- addListener(ColumnResizeEvent.class, listener, COLUMN_RESIZE_METHOD);
- }
-
- /**
- * Removes a previously registered column resize listener.
- *
- * @param listener
- * the listener to remove
- */
- public void removeColumnResizeListener(ColumnResizeListener listener) {
- removeListener(ColumnResizeEvent.class, listener, COLUMN_RESIZE_METHOD);
- }
-
- /**
- * Gets the {@link KeyMapper } being used by the data source.
- *
- * @return the key mapper being used by the data source
- */
- KeyMapper<Object> getKeyMapper() {
- return datasourceExtension.getKeyMapper();
- }
-
- /**
- * Adds a renderer to this grid's connector hierarchy.
- *
- * @param renderer
- * the renderer to add
- */
- void addRenderer(Renderer<?> renderer) {
- addExtension(renderer);
- }
-
- /**
- * Sets the current sort order using the fluid Sort API. Read the
- * documentation for {@link Sort} for more information.
- * <p>
- * <em>Note:</em> Sorting by a property that has no column in Grid will hide
- * all possible sorting indicators.
- *
- * @param s
- * a sort instance
- *
- * @throws IllegalStateException
- * if container is not sortable (does not implement
- * Container.Sortable)
- * @throws IllegalArgumentException
- * if trying to sort by non-existing property
- */
- public void sort(Sort s) {
- setSortOrder(s.build());
- }
-
- /**
- * Sort this Grid in ascending order by a specified property.
- * <p>
- * <em>Note:</em> Sorting by a property that has no column in Grid will hide
- * all possible sorting indicators.
- *
- * @param propertyId
- * a property ID
- *
- * @throws IllegalStateException
- * if container is not sortable (does not implement
- * Container.Sortable)
- * @throws IllegalArgumentException
- * if trying to sort by non-existing property
- */
- public void sort(Object propertyId) {
- sort(propertyId, SortDirection.ASCENDING);
- }
-
- /**
- * Sort this Grid in user-specified {@link SortOrder} by a property.
- * <p>
- * <em>Note:</em> Sorting by a property that has no column in Grid will hide
- * all possible sorting indicators.
- *
- * @param propertyId
- * a property ID
- * @param direction
- * a sort order value (ascending/descending)
- *
- * @throws IllegalStateException
- * if container is not sortable (does not implement
- * Container.Sortable)
- * @throws IllegalArgumentException
- * if trying to sort by non-existing property
- */
- public void sort(Object propertyId, SortDirection direction) {
- sort(Sort.by(propertyId, direction));
- }
-
- /**
- * Clear the current sort order, and re-sort the grid.
- */
- public void clearSortOrder() {
- sortOrder.clear();
- sort(false);
- }
-
- /**
- * Sets the sort order to use.
- * <p>
- * <em>Note:</em> Sorting by a property that has no column in Grid will hide
- * all possible sorting indicators.
- *
- * @param order
- * a sort order list.
- *
- * @throws IllegalStateException
- * if container is not sortable (does not implement
- * Container.Sortable)
- * @throws IllegalArgumentException
- * if order is null or trying to sort by non-existing property
- */
- public void setSortOrder(List<SortOrder> order) {
- setSortOrder(order, false);
- }
-
- private void setSortOrder(List<SortOrder> order, boolean userOriginated)
- throws IllegalStateException, IllegalArgumentException {
- if (!(getContainerDataSource() instanceof Container.Sortable)) {
- throw new IllegalStateException(
- "Attached container is not sortable (does not implement Container.Sortable)");
- }
-
- if (order == null) {
- throw new IllegalArgumentException("Order list may not be null!");
- }
-
- sortOrder.clear();
-
- Collection<?> sortableProps = ((Container.Sortable) getContainerDataSource())
- .getSortableContainerPropertyIds();
-
- for (SortOrder o : order) {
- if (!sortableProps.contains(o.getPropertyId())) {
- throw new IllegalArgumentException("Property "
- + o.getPropertyId()
- + " does not exist or is not sortable in the current container");
- }
- }
-
- sortOrder.addAll(order);
- sort(userOriginated);
- }
-
- /**
- * Get the current sort order list.
- *
- * @return a sort order list
- */
- public List<SortOrder> getSortOrder() {
- return Collections.unmodifiableList(sortOrder);
- }
-
- /**
- * Apply sorting to data source.
- */
- private void sort(boolean userOriginated) {
-
- Container c = getContainerDataSource();
- if (c instanceof Container.Sortable) {
- Container.Sortable cs = (Container.Sortable) c;
-
- final int items = sortOrder.size();
- Object[] propertyIds = new Object[items];
- boolean[] directions = new boolean[items];
-
- SortDirection[] stateDirs = new SortDirection[items];
-
- for (int i = 0; i < items; ++i) {
- SortOrder order = sortOrder.get(i);
-
- stateDirs[i] = order.getDirection();
- propertyIds[i] = order.getPropertyId();
- switch (order.getDirection()) {
- case ASCENDING:
- directions[i] = true;
- break;
- case DESCENDING:
- directions[i] = false;
- break;
- default:
- throw new IllegalArgumentException("getDirection() of "
- + order + " returned an unexpected value");
- }
- }
-
- cs.sort(propertyIds, directions);
-
- if (columns.keySet().containsAll(Arrays.asList(propertyIds))) {
- String[] columnKeys = new String[items];
- for (int i = 0; i < items; ++i) {
- columnKeys[i] = this.columnKeys.key(propertyIds[i]);
- }
- getState().sortColumns = columnKeys;
- getState(false).sortDirs = stateDirs;
- } else {
- // Not all sorted properties are in Grid. Remove any indicators.
- getState().sortColumns = new String[] {};
- getState(false).sortDirs = new SortDirection[] {};
- }
- fireEvent(new SortEvent(this, new ArrayList<SortOrder>(sortOrder),
- userOriginated));
- } else {
- throw new IllegalStateException(
- "Container is not sortable (does not implement Container.Sortable)");
- }
- }
-
- /**
- * Adds a sort order change listener that gets notified when the sort order
- * changes.
- *
- * @param listener
- * the sort order change listener to add
- */
- @Override
- public void addSortListener(SortListener listener) {
- addListener(SortEvent.class, listener, SORT_ORDER_CHANGE_METHOD);
- }
-
- /**
- * Removes a sort order change listener previously added using
- * {@link #addSortListener(SortListener)}.
- *
- * @param listener
- * the sort order change listener to remove
- */
- @Override
- public void removeSortListener(SortListener listener) {
- removeListener(SortEvent.class, listener, SORT_ORDER_CHANGE_METHOD);
- }
-
- /* Grid Headers */
-
- /**
- * Returns the header section of this grid. The default header contains a
- * single row displaying the column captions.
- *
- * @return the header
- */
- protected Header getHeader() {
- return header;
- }
-
- /**
- * Gets the header row at given index.
- *
- * @param rowIndex
- * 0 based index for row. Counted from top to bottom
- * @return header row at given index
- * @throws IllegalArgumentException
- * if no row exists at given index
- */
- public HeaderRow getHeaderRow(int rowIndex) {
- return header.getRow(rowIndex);
- }
-
- /**
- * Inserts a new row at the given position to the header section. Shifts the
- * row currently at that position and any subsequent rows down (adds one to
- * their indices).
- *
- * @param index
- * the position at which to insert the row
- * @return the new row
- *
- * @throws IllegalArgumentException
- * if the index is less than 0 or greater than row count
- * @see #appendHeaderRow()
- * @see #prependHeaderRow()
- * @see #removeHeaderRow(HeaderRow)
- * @see #removeHeaderRow(int)
- */
- public HeaderRow addHeaderRowAt(int index) {
- return header.addRowAt(index);
- }
-
- /**
- * Adds a new row at the bottom of the header section.
- *
- * @return the new row
- * @see #prependHeaderRow()
- * @see #addHeaderRowAt(int)
- * @see #removeHeaderRow(HeaderRow)
- * @see #removeHeaderRow(int)
- */
- public HeaderRow appendHeaderRow() {
- return header.appendRow();
- }
-
- /**
- * Returns the current default row of the header section. The default row is
- * a special header row providing a user interface for sorting columns.
- * Setting a header text for column updates cells in the default header.
- *
- * @return the default row or null if no default row set
- */
- public HeaderRow getDefaultHeaderRow() {
- return header.getDefaultRow();
- }
-
- /**
- * Gets the row count for the header section.
- *
- * @return row count
- */
- public int getHeaderRowCount() {
- return header.getRowCount();
- }
-
- /**
- * Adds a new row at the top of the header section.
- *
- * @return the new row
- * @see #appendHeaderRow()
- * @see #addHeaderRowAt(int)
- * @see #removeHeaderRow(HeaderRow)
- * @see #removeHeaderRow(int)
- */
- public HeaderRow prependHeaderRow() {
- return header.prependRow();
- }
-
- /**
- * Removes the given row from the header section.
- *
- * @param row
- * the row to be removed
- *
- * @throws IllegalArgumentException
- * if the row does not exist in this section
- * @see #removeHeaderRow(int)
- * @see #addHeaderRowAt(int)
- * @see #appendHeaderRow()
- * @see #prependHeaderRow()
- */
- public void removeHeaderRow(HeaderRow row) {
- header.removeRow(row);
- }
-
- /**
- * Removes the row at the given position from the header section.
- *
- * @param rowIndex
- * the position of the row
- *
- * @throws IllegalArgumentException
- * if no row exists at given index
- * @see #removeHeaderRow(HeaderRow)
- * @see #addHeaderRowAt(int)
- * @see #appendHeaderRow()
- * @see #prependHeaderRow()
- */
- public void removeHeaderRow(int rowIndex) {
- header.removeRow(rowIndex);
- }
-
- /**
- * Sets the default row of the header. The default row is a special header
- * row providing a user interface for sorting columns.
- *
- * @param row
- * the new default row, or null for no default row
- *
- * @throws IllegalArgumentException
- * header does not contain the row
- */
- public void setDefaultHeaderRow(HeaderRow row) {
- header.setDefaultRow(row);
- }
-
- /**
- * Sets the visibility of the header section.
- *
- * @param visible
- * true to show header section, false to hide
- */
- public void setHeaderVisible(boolean visible) {
- header.setVisible(visible);
- }
-
- /**
- * Returns the visibility of the header section.
- *
- * @return true if visible, false otherwise.
- */
- public boolean isHeaderVisible() {
- return header.isVisible();
- }
-
- /* Grid Footers */
-
- /**
- * Returns the footer section of this grid. The default header contains a
- * single row displaying the column captions.
- *
- * @return the footer
- */
- protected Footer getFooter() {
- return footer;
- }
-
- /**
- * Gets the footer row at given index.
- *
- * @param rowIndex
- * 0 based index for row. Counted from top to bottom
- * @return footer row at given index
- * @throws IllegalArgumentException
- * if no row exists at given index
- */
- public FooterRow getFooterRow(int rowIndex) {
- return footer.getRow(rowIndex);
- }
-
- /**
- * Inserts a new row at the given position to the footer section. Shifts the
- * row currently at that position and any subsequent rows down (adds one to
- * their indices).
- *
- * @param index
- * the position at which to insert the row
- * @return the new row
- *
- * @throws IllegalArgumentException
- * if the index is less than 0 or greater than row count
- * @see #appendFooterRow()
- * @see #prependFooterRow()
- * @see #removeFooterRow(FooterRow)
- * @see #removeFooterRow(int)
- */
- public FooterRow addFooterRowAt(int index) {
- return footer.addRowAt(index);
- }
-
- /**
- * Adds a new row at the bottom of the footer section.
- *
- * @return the new row
- * @see #prependFooterRow()
- * @see #addFooterRowAt(int)
- * @see #removeFooterRow(FooterRow)
- * @see #removeFooterRow(int)
- */
- public FooterRow appendFooterRow() {
- return footer.appendRow();
- }
-
- /**
- * Gets the row count for the footer.
- *
- * @return row count
- */
- public int getFooterRowCount() {
- return footer.getRowCount();
- }
-
- /**
- * Adds a new row at the top of the footer section.
- *
- * @return the new row
- * @see #appendFooterRow()
- * @see #addFooterRowAt(int)
- * @see #removeFooterRow(FooterRow)
- * @see #removeFooterRow(int)
- */
- public FooterRow prependFooterRow() {
- return footer.prependRow();
- }
-
- /**
- * Removes the given row from the footer section.
- *
- * @param row
- * the row to be removed
- *
- * @throws IllegalArgumentException
- * if the row does not exist in this section
- * @see #removeFooterRow(int)
- * @see #addFooterRowAt(int)
- * @see #appendFooterRow()
- * @see #prependFooterRow()
- */
- public void removeFooterRow(FooterRow row) {
- footer.removeRow(row);
- }
-
- /**
- * Removes the row at the given position from the footer section.
- *
- * @param rowIndex
- * the position of the row
- *
- * @throws IllegalArgumentException
- * if no row exists at given index
- * @see #removeFooterRow(FooterRow)
- * @see #addFooterRowAt(int)
- * @see #appendFooterRow()
- * @see #prependFooterRow()
- */
- public void removeFooterRow(int rowIndex) {
- footer.removeRow(rowIndex);
- }
-
- /**
- * Sets the visibility of the footer section.
- *
- * @param visible
- * true to show footer section, false to hide
- */
- public void setFooterVisible(boolean visible) {
- footer.setVisible(visible);
- }
-
- /**
- * Returns the visibility of the footer section.
- *
- * @return true if visible, false otherwise.
- */
- public boolean isFooterVisible() {
- return footer.isVisible();
- }
-
- private void addComponent(Component c) {
- extensionComponents.add(c);
- c.setParent(this);
- markAsDirty();
- }
-
- private void removeComponent(Component c) {
- extensionComponents.remove(c);
- c.setParent(null);
- markAsDirty();
- }
-
- @Override
- public Iterator<Component> iterator() {
- // This is a hash set to avoid adding header/footer components inside
- // merged cells multiple times
- LinkedHashSet<Component> componentList = new LinkedHashSet<Component>();
-
- Header header = getHeader();
- for (int i = 0; i < header.getRowCount(); ++i) {
- HeaderRow row = header.getRow(i);
- for (Object propId : columns.keySet()) {
- HeaderCell cell = row.getCell(propId);
- if (cell.getCellState().type == GridStaticCellType.WIDGET) {
- componentList.add(cell.getComponent());
- }
- }
- }
-
- Footer footer = getFooter();
- for (int i = 0; i < footer.getRowCount(); ++i) {
- FooterRow row = footer.getRow(i);
- for (Object propId : columns.keySet()) {
- FooterCell cell = row.getCell(propId);
- if (cell.getCellState().type == GridStaticCellType.WIDGET) {
- componentList.add(cell.getComponent());
- }
- }
- }
-
- componentList.addAll(getEditorFields());
-
- componentList.addAll(extensionComponents);
-
- return componentList.iterator();
- }
-
- @Override
- public boolean isRendered(Component childComponent) {
- if (getEditorFields().contains(childComponent)) {
- // Only render editor fields if the editor is open
- return isEditorActive();
- } else {
- // TODO Header and footer components should also only be rendered if
- // the header/footer is visible
- return true;
- }
- }
-
- EditorClientRpc getEditorRpc() {
- return getRpcProxy(EditorClientRpc.class);
- }
-
- /**
- * Sets the {@code CellDescriptionGenerator} instance for generating
- * optional descriptions (tooltips) for individual Grid cells. If a
- * {@link RowDescriptionGenerator} is also set, the row description it
- * generates is displayed for cells for which {@code generator} returns
- * <code>null</code>.
- * <p>
- * The descriptions are rendered in the browser as HTML and the developer is
- * responsible for ensuring no harmful HTML is used.
- *
- * @param generator
- * the description generator to use or <code>null</code> to
- * remove a previously set generator if any
- *
- * @see #setCellDescriptionGenerator(CellDescriptionGenerator, ContentMode)
- * @see #setRowDescriptionGenerator(RowDescriptionGenerator)
- *
- * @since 7.6
- */
-
- public void setCellDescriptionGenerator(CellDescriptionGenerator generator) {
- /*
- * When porting this to the v7 version in Framework 8, the default
- * should be changed to PREFORMATTED to preserve the more secure
- * default that has accidentally been used there.
- */
- setCellDescriptionGenerator(generator, ContentMode.HTML);
- }
-
- /**
- * Sets the {@code CellDescriptionGenerator} instance and content mode for
- * generating optional descriptions (tooltips) for individual Grid cells. If
- * a {@link RowDescriptionGenerator} is also set, the row description it
- * generates is displayed for cells for which {@code generator} returns
- * <code>null</code>.
- *
- * @param generator
- * the description generator to use or <code>null</code> to
- * remove a previously set generator if any
- * @param contentMode
- * the content mode for cell tooltips, not <code>null</code>
- * @see #setRowDescriptionGenerator(RowDescriptionGenerator)
- *
- * @since 7.7.14
- */
- public void setCellDescriptionGenerator(CellDescriptionGenerator generator,
- ContentMode contentMode) {
- if (contentMode == null) {
- throw new IllegalArgumentException("Content mode cannot be null");
- }
- cellDescriptionGenerator = generator;
- getState().hasDescriptions = (generator != null || rowDescriptionGenerator != null);
- getState().cellTooltipContentMode = contentMode;
- datasourceExtension.refreshCache();
- }
-
- /**
- * Returns the {@code CellDescriptionGenerator} instance used to generate
- * descriptions (tooltips) for Grid cells.
- *
- * @return the description generator or {@code null} if no generator is set
- *
- * @since 7.6
- */
- public CellDescriptionGenerator getCellDescriptionGenerator() {
- return cellDescriptionGenerator;
- }
-
- /**
- * Gets the content mode used for cell descriptions.
- *
- * @return the content mode used for cell descriptions, not
- * <code>null</code>
- * @see #setCellDescriptionGenerator(CellDescriptionGenerator, ContentMode)
- */
- public ContentMode getCellDescriptionContentMode() {
- return getState(false).cellTooltipContentMode;
- }
-
- /**
- * Sets the {@code RowDescriptionGenerator} instance for generating optional
- * descriptions (tooltips) for Grid rows. If a
- * {@link CellDescriptionGenerator} is also set, the row description
- * generated by {@code generator} is used for cells for which the cell
- * description generator returns <code>null</code>.
- * <p>
- * The descriptions are rendered in the browser as HTML and the developer is
- * responsible for ensuring no harmful HTML is used.
- *
- * @param generator
- * the description generator to use or <code>null</code> to
- * remove a previously set generator if any
- *
- * @see #setRowDescriptionGenerator(RowDescriptionGenerator, ContentMode)
- * @see #setCellDescriptionGenerator(CellDescriptionGenerator)
- *
- * @since 7.6
- */
- public void setRowDescriptionGenerator(RowDescriptionGenerator generator) {
- /*
- * When porting this to the v7 version in Framework 8, the default
- * should be changed to PREFORMATTED to preserve the more secure
- * default that has accidentally been used there.
- */
- setRowDescriptionGenerator(generator, ContentMode.HTML);
- }
-
- /**
- * Sets the {@code RowDescriptionGenerator} instance for generating optional
- * descriptions (tooltips) for Grid rows. If a
- * {@link CellDescriptionGenerator} is also set, the row description
- * generated by {@code generator} is used for cells for which the cell
- * description generator returns <code>null</code>.
- *
- * @param generator
- * the description generator to use or <code>null</code> to
- * remove a previously set generator if any
- * @param contentMode
- * the content mode for row tooltips, not <code>null</code>
- *
- * @see #setCellDescriptionGenerator(CellDescriptionGenerator)
- *
- * @since 7.7.14
- */
- public void setRowDescriptionGenerator(RowDescriptionGenerator generator,
- ContentMode contentMode) {
- if (contentMode == null) {
- throw new IllegalArgumentException("Content mode cannot be null");
- }
- rowDescriptionGenerator = generator;
- getState().hasDescriptions = (generator != null
- || cellDescriptionGenerator != null);
- getState().rowTooltipContentMode = contentMode;
- datasourceExtension.refreshCache();
- }
-
- /**
- * Gets the content mode used for row descriptions.
- *
- * @return the content mode used for row descriptions, not <code>null</code>
- * @see #setRowDescriptionGenerator(RowDescriptionGenerator, ContentMode)
- */
- public ContentMode getRowDescriptionContentMode() {
- return getState(false).rowTooltipContentMode;
- }
-
- /**
- * Returns the {@code RowDescriptionGenerator} instance used to generate
- * descriptions (tooltips) for Grid rows
- *
- * @return the description generator or {@code} null if no generator is set
- *
- * @since 7.6
- */
- public RowDescriptionGenerator getRowDescriptionGenerator() {
- return rowDescriptionGenerator;
- }
-
- /**
- * Sets the style generator that is used for generating styles for cells
- *
- * @param cellStyleGenerator
- * the cell style generator to set, or <code>null</code> to
- * remove a previously set generator
- */
- public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) {
- this.cellStyleGenerator = cellStyleGenerator;
- datasourceExtension.refreshCache();
- }
-
- /**
- * Gets the style generator that is used for generating styles for cells
- *
- * @return the cell style generator, or <code>null</code> if no generator is
- * set
- */
- public CellStyleGenerator getCellStyleGenerator() {
- return cellStyleGenerator;
- }
-
- /**
- * Sets the style generator that is used for generating styles for rows
- *
- * @param rowStyleGenerator
- * the row style generator to set, or <code>null</code> to remove
- * a previously set generator
- */
- public void setRowStyleGenerator(RowStyleGenerator rowStyleGenerator) {
- this.rowStyleGenerator = rowStyleGenerator;
- datasourceExtension.refreshCache();
- }
-
- /**
- * Gets the style generator that is used for generating styles for rows
- *
- * @return the row style generator, or <code>null</code> if no generator is
- * set
- */
- public RowStyleGenerator getRowStyleGenerator() {
- return rowStyleGenerator;
- }
-
- /**
- * Adds a row to the underlying container. The order of the parameters
- * should match the current visible column order.
- * <p>
- * Please note that it's generally only safe to use this method during
- * initialization. After Grid has been initialized and the visible column
- * order might have been changed, it's better to instead add items directly
- * to the underlying container and use {@link Item#getItemProperty(Object)}
- * to make sure each value is assigned to the intended property.
- *
- * @param values
- * the cell values of the new row, in the same order as the
- * visible column order, not <code>null</code>.
- * @return the item id of the new row
- * @throws IllegalArgumentException
- * if values is null
- * @throws IllegalArgumentException
- * if its length does not match the number of visible columns
- * @throws IllegalArgumentException
- * if a parameter value is not an instance of the corresponding
- * property type
- * @throws UnsupportedOperationException
- * if the container does not support adding new items
- */
- public Object addRow(Object... values) {
- if (values == null) {
- throw new IllegalArgumentException("Values cannot be null");
- }
-
- Indexed dataSource = getContainerDataSource();
- List<String> columnOrder = getState(false).columnOrder;
-
- if (values.length != columnOrder.size()) {
- throw new IllegalArgumentException(
- "There are " + columnOrder.size() + " visible columns, but "
- + values.length + " cell values were provided.");
- }
-
- // First verify all parameter types
- for (int i = 0; i < columnOrder.size(); i++) {
- Object propertyId = getPropertyIdByColumnId(columnOrder.get(i));
-
- Class<?> propertyType = dataSource.getType(propertyId);
- if (values[i] != null && !propertyType.isInstance(values[i])) {
- throw new IllegalArgumentException("Parameter " + i + "("
- + values[i] + ") is not an instance of "
- + propertyType.getCanonicalName());
- }
- }
-
- Object itemId = dataSource.addItem();
- try {
- Item item = dataSource.getItem(itemId);
- for (int i = 0; i < columnOrder.size(); i++) {
- Object propertyId = getPropertyIdByColumnId(columnOrder.get(i));
- Property<Object> property = item.getItemProperty(propertyId);
- property.setValue(values[i]);
- }
- } catch (RuntimeException e) {
- try {
- dataSource.removeItem(itemId);
- } catch (Exception e2) {
- getLogger().log(Level.SEVERE,
- "Error recovering from exception in addRow", e);
- }
- throw e;
- }
-
- return itemId;
- }
-
- /**
- * Refreshes, i.e. causes the client side to re-render the rows with the
- * given item ids.
- * <p>
- * Calling this for a row which is not currently rendered on the client side
- * has no effect.
- *
- * @param itemIds
- * the item id(s) of the row to refresh.
- */
- public void refreshRows(Object... itemIds) {
- for (Object itemId : itemIds) {
- datasourceExtension.updateRowData(itemId);
- }
- }
-
- /**
- * Refreshes, i.e. causes the client side to re-render all rows.
- *
- * @since 7.7.7
- */
- public void refreshAllRows() {
- datasourceExtension.refreshCache();
- }
-
- private static Logger getLogger() {
- return Logger.getLogger(Grid.class.getName());
- }
-
- /**
- * Sets whether or not the item editor UI is enabled for this grid. When the
- * editor is enabled, the user can open it by double-clicking a row or
- * hitting enter when a row is focused. The editor can also be opened
- * programmatically using the {@link #editItem(Object)} method.
- *
- * @param isEnabled
- * <code>true</code> to enable the feature, <code>false</code>
- * otherwise
- * @throws IllegalStateException
- * if an item is currently being edited
- *
- * @see #getEditedItemId()
- */
- public void setEditorEnabled(boolean isEnabled)
- throws IllegalStateException {
- if (isEditorActive()) {
- throw new IllegalStateException(
- "Cannot disable the editor while an item ("
- + getEditedItemId() + ") is being edited");
- }
- if (isEditorEnabled() != isEnabled) {
- getState().editorEnabled = isEnabled;
- }
- }
-
- /**
- * Checks whether the item editor UI is enabled for this grid.
- *
- * @return <code>true</code> iff the editor is enabled for this grid
- *
- * @see #setEditorEnabled(boolean)
- * @see #getEditedItemId()
- */
- public boolean isEditorEnabled() {
- return getState(false).editorEnabled;
- }
-
- /**
- * Gets the id of the item that is currently being edited.
- *
- * @return the id of the item that is currently being edited, or
- * <code>null</code> if no item is being edited at the moment
- */
- public Object getEditedItemId() {
- return editedItemId;
- }
-
- /**
- * Gets the field group that is backing the item editor of this grid.
- *
- * @return the backing field group
- */
- public FieldGroup getEditorFieldGroup() {
- return editorFieldGroup;
- }
-
- /**
- * Sets the field group that is backing the item editor of this grid.
- *
- * @param fieldGroup
- * the backing field group
- *
- * @throws IllegalStateException
- * if the editor is currently active
- */
- public void setEditorFieldGroup(FieldGroup fieldGroup) {
- if (isEditorActive()) {
- throw new IllegalStateException(
- "Cannot change field group while an item ("
- + getEditedItemId() + ") is being edited");
- }
- editorFieldGroup = fieldGroup;
- }
-
- /**
- * Returns whether an item is currently being edited in the editor.
- *
- * @return true iff the editor is open
- */
- public boolean isEditorActive() {
- return editorActive;
- }
-
- private void checkColumnExists(Object propertyId) {
- if (getColumn(propertyId) == null) {
- throw new IllegalArgumentException(
- "There is no column with the property id " + propertyId);
- }
- }
-
- private Field<?> getEditorField(Object propertyId) {
- checkColumnExists(propertyId);
-
- if (!getColumn(propertyId).isEditable()) {
- return null;
- }
-
- Field<?> editor = editorFieldGroup.getField(propertyId);
-
- // If field group has no field for this property, see if we have it
- // stored
- if (editor == null) {
- editor = editorFields.get(propertyId);
- if (editor != null) {
- editorFieldGroup.bind(editor, propertyId);
- }
- }
-
- // Otherwise try to build one
- try {
- if (editor == null) {
- editor = editorFieldGroup.buildAndBind(propertyId);
- }
- } finally {
- if (editor == null) {
- editor = editorFieldGroup.getField(propertyId);
- }
-
- if (editor != null && editor.getParent() != Grid.this) {
- assert editor.getParent() == null;
- editor.setParent(this);
- }
- }
- return editor;
- }
-
- /**
- * Opens the editor interface for the provided item. Scrolls the Grid to
- * bring the item to view if it is not already visible.
- *
- * Note that any cell content rendered by a WidgetRenderer will not be
- * visible in the editor row.
- *
- * @param itemId
- * the id of the item to edit
- * @throws IllegalStateException
- * if the editor is not enabled or already editing an item in
- * buffered mode
- * @throws IllegalArgumentException
- * if the {@code itemId} is not in the backing container
- * @see #setEditorEnabled(boolean)
- */
- public void editItem(Object itemId)
- throws IllegalStateException, IllegalArgumentException {
- if (!isEditorEnabled()) {
- throw new IllegalStateException("Item editor is not enabled");
- } else if (isEditorBuffered() && editedItemId != null) {
- throw new IllegalStateException("Editing item " + itemId
- + " failed. Item editor is already editing item "
- + editedItemId);
- } else if (!getContainerDataSource().containsId(itemId)) {
- throw new IllegalArgumentException("Item with id " + itemId
- + " not found in current container");
- }
- editedItemId = itemId;
- getEditorRpc().bind(getContainerDataSource().indexOfId(itemId));
- }
-
- protected void doEditItem() {
- Item item = getContainerDataSource().getItem(editedItemId);
-
- editorFieldGroup.setItemDataSource(item);
-
- for (Column column : getColumns()) {
- column.getState().editorConnector = item
- .getItemProperty(column.getPropertyId()) == null ? null
- : getEditorField(column.getPropertyId());
- }
-
- editorActive = true;
- // Must ensure that all fields, recursively, are sent to the client
- // This is needed because the fields are hidden using isRendered
- for (Field<?> f : getEditorFields()) {
- f.markAsDirtyRecursive();
- }
-
- if (datasource instanceof ItemSetChangeNotifier) {
- ((ItemSetChangeNotifier) datasource)
- .addItemSetChangeListener(editorClosingItemSetListener);
- }
- }
-
- private void setEditorField(Object propertyId, Field<?> field) {
- checkColumnExists(propertyId);
-
- Field<?> oldField = editorFieldGroup.getField(propertyId);
- if (oldField != null) {
- editorFieldGroup.unbind(oldField);
- oldField.setParent(null);
- }
-
- if (field != null) {
- field.setParent(this);
- editorFieldGroup.bind(field, propertyId);
- }
-
- // Store field for this property for future reference
- editorFields.put(propertyId, field);
- }
-
- /**
- * Saves all changes done to the bound fields.
- * <p>
- * <em>Note:</em> This is a pass-through call to the backing field group.
- *
- * @throws CommitException
- * If the commit was aborted
- *
- * @see FieldGroup#commit()
- */
- public void saveEditor() throws CommitException {
- try {
- editorSaving = true;
- editorFieldGroup.commit();
- } finally {
- editorSaving = false;
- }
- }
-
- /**
- * Cancels the currently active edit if any. Hides the editor and discards
- * possible unsaved changes in the editor fields.
- */
- public void cancelEditor() {
- if (editorSaving) {
- // If the editor is already saving the values, it's too late to
- // cancel it. This prevents item set changes from propagating during
- // save, causing discard to be run during commit.
- return;
- }
-
- if (isEditorActive()) {
- getEditorRpc()
- .cancel(getContainerDataSource().indexOfId(editedItemId));
- doCancelEditor();
- }
- }
-
- protected void doCancelEditor() {
- editedItemId = null;
- editorActive = false;
- editorFieldGroup.discard();
- editorFieldGroup.setItemDataSource(null);
-
- if (datasource instanceof ItemSetChangeNotifier) {
- ((ItemSetChangeNotifier) datasource)
- .removeItemSetChangeListener(editorClosingItemSetListener);
- }
-
- // Mark Grid as dirty so the client side gets to know that the editors
- // are no longer attached
- markAsDirty();
- }
-
- void resetEditor() {
- if (isEditorActive()) {
- /*
- * Simply force cancel the editing; throwing here would just make
- * Grid.setContainerDataSource semantics more complicated.
- */
- cancelEditor();
- }
- for (Field<?> editor : getEditorFields()) {
- editor.setParent(null);
- }
-
- editedItemId = null;
- editorActive = false;
- editorFieldGroup = new CustomFieldGroup();
- }
-
- /**
- * Gets a collection of all fields bound to the item editor of this grid.
- * <p>
- * When {@link #editItem(Object) editItem} is called, fields are
- * automatically created and bound to any unbound properties.
- *
- * @return a collection of all the fields bound to the item editor
- */
- Collection<Field<?>> getEditorFields() {
- Collection<Field<?>> fields = editorFieldGroup.getFields();
- assert allAttached(fields);
- return fields;
- }
-
- private boolean allAttached(Collection<? extends Component> components) {
- for (Component component : components) {
- if (component.getParent() != this) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Sets the field factory for the {@link FieldGroup}. The field factory is
- * only used when {@link FieldGroup} creates a new field.
- * <p>
- * <em>Note:</em> This is a pass-through call to the backing field group.
- *
- * @param fieldFactory
- * The field factory to use
- */
- public void setEditorFieldFactory(FieldGroupFieldFactory fieldFactory) {
- editorFieldGroup.setFieldFactory(fieldFactory);
- }
-
- /**
- * Sets the error handler for the editor.
- *
- * The error handler is called whenever there is an exception in the editor.
- *
- * @param editorErrorHandler
- * The editor error handler to use
- * @throws IllegalArgumentException
- * if the error handler is null
- */
- public void setEditorErrorHandler(EditorErrorHandler editorErrorHandler)
- throws IllegalArgumentException {
- if (editorErrorHandler == null) {
- throw new IllegalArgumentException(
- "The error handler cannot be null");
- }
- this.editorErrorHandler = editorErrorHandler;
- }
-
- /**
- * Gets the error handler used for the editor
- *
- * @see #setErrorHandler(com.vaadin.server.ErrorHandler)
- * @return the editor error handler, never null
- */
- public EditorErrorHandler getEditorErrorHandler() {
- return editorErrorHandler;
- }
-
- /**
- * Gets the field factory for the {@link FieldGroup}. The field factory is
- * only used when {@link FieldGroup} creates a new field.
- * <p>
- * <em>Note:</em> This is a pass-through call to the backing field group.
- *
- * @return The field factory in use
- */
- public FieldGroupFieldFactory getEditorFieldFactory() {
- return editorFieldGroup.getFieldFactory();
- }
-
- /**
- * Sets the caption on the save button in the Grid editor.
- *
- * @param saveCaption
- * the caption to set
- * @throws IllegalArgumentException
- * if {@code saveCaption} is {@code null}
- */
- public void setEditorSaveCaption(String saveCaption)
- throws IllegalArgumentException {
- if (saveCaption == null) {
- throw new IllegalArgumentException("Save caption cannot be null");
- }
- getState().editorSaveCaption = saveCaption;
- }
-
- /**
- * Gets the current caption of the save button in the Grid editor.
- *
- * @return the current caption of the save button
- */
- public String getEditorSaveCaption() {
- return getState(false).editorSaveCaption;
- }
-
- /**
- * Sets the caption on the cancel button in the Grid editor.
- *
- * @param cancelCaption
- * the caption to set
- * @throws IllegalArgumentException
- * if {@code cancelCaption} is {@code null}
- */
- public void setEditorCancelCaption(String cancelCaption)
- throws IllegalArgumentException {
- if (cancelCaption == null) {
- throw new IllegalArgumentException("Cancel caption cannot be null");
- }
- getState().editorCancelCaption = cancelCaption;
- }
-
- /**
- * Gets the current caption of the cancel button in the Grid editor.
- *
- * @return the current caption of the cancel button
- */
- public String getEditorCancelCaption() {
- return getState(false).editorCancelCaption;
- }
-
- /**
- * Sets the buffered editor mode. The default mode is buffered (
- * <code>true</code>).
- *
- * @since 7.6
- * @param editorBuffered
- * <code>true</code> to enable buffered editor,
- * <code>false</code> to disable it
- * @throws IllegalStateException
- * If editor is active while attempting to change the buffered
- * mode.
- */
- public void setEditorBuffered(boolean editorBuffered)
- throws IllegalStateException {
- if (isEditorActive()) {
- throw new IllegalStateException(
- "Can't change editor unbuffered mode while editor is active.");
- }
- getState().editorBuffered = editorBuffered;
- editorFieldGroup.setBuffered(editorBuffered);
- }
-
- /**
- * Gets the buffered editor mode.
- *
- * @since 7.6
- * @return <code>true</code> if buffered editor is enabled,
- * <code>false</code> otherwise
- */
- public boolean isEditorBuffered() {
- return getState(false).editorBuffered;
- }
-
- @Override
- public void addItemClickListener(ItemClickListener listener) {
- addListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
- listener, ItemClickEvent.ITEM_CLICK_METHOD);
- }
-
- @Override
- @Deprecated
- public void addListener(ItemClickListener listener) {
- addItemClickListener(listener);
- }
-
- @Override
- public void removeItemClickListener(ItemClickListener listener) {
- removeListener(GridConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
- listener);
- }
-
- @Override
- @Deprecated
- public void removeListener(ItemClickListener listener) {
- removeItemClickListener(listener);
- }
-
- /**
- * Requests that the column widths should be recalculated.
- * <p>
- * In most cases Grid will know when column widths need to be recalculated
- * but this method can be used to force recalculation in situations when
- * grid does not recalculate automatically.
- *
- * @since 7.4.1
- */
- public void recalculateColumnWidths() {
- getRpcProxy(GridClientRpc.class).recalculateColumnWidths();
- }
-
- /**
- * Registers a new column visibility change listener
- *
- * @since 7.5.0
- * @param listener
- * the listener to register
- */
- public void addColumnVisibilityChangeListener(
- ColumnVisibilityChangeListener listener) {
- addListener(ColumnVisibilityChangeEvent.class, listener,
- COLUMN_VISIBILITY_METHOD);
- }
-
- /**
- * Removes a previously registered column visibility change listener
- *
- * @since 7.5.0
- * @param listener
- * the listener to remove
- */
- public void removeColumnVisibilityChangeListener(
- ColumnVisibilityChangeListener listener) {
- removeListener(ColumnVisibilityChangeEvent.class, listener,
- COLUMN_VISIBILITY_METHOD);
- }
-
- private void fireColumnVisibilityChangeEvent(Column column, boolean hidden,
- boolean isUserOriginated) {
- fireEvent(new ColumnVisibilityChangeEvent(this, column, hidden,
- isUserOriginated));
- }
-
- /**
- * Sets a new details generator for row details.
- * <p>
- * The currently opened row details will be re-rendered.
- *
- * @since 7.5.0
- * @param detailsGenerator
- * the details generator to set
- * @throws IllegalArgumentException
- * if detailsGenerator is <code>null</code>;
- */
- public void setDetailsGenerator(DetailsGenerator detailsGenerator)
- throws IllegalArgumentException {
- detailComponentManager.setDetailsGenerator(detailsGenerator);
- }
-
- /**
- * Gets the current details generator for row details.
- *
- * @since 7.5.0
- * @return the detailsGenerator the current details generator
- */
- public DetailsGenerator getDetailsGenerator() {
- return detailComponentManager.getDetailsGenerator();
- }
-
- /**
- * Shows or hides the details for a specific item.
- *
- * @since 7.5.0
- * @param itemId
- * the id of the item for which to set details visibility
- * @param visible
- * <code>true</code> to show the details, or <code>false</code>
- * to hide them
- */
- public void setDetailsVisible(Object itemId, boolean visible) {
- detailComponentManager.setDetailsVisible(itemId, visible);
- }
-
- /**
- * Checks whether details are visible for the given item.
- *
- * @since 7.5.0
- * @param itemId
- * the id of the item for which to check details visibility
- * @return <code>true</code> iff the details are visible
- */
- public boolean isDetailsVisible(Object itemId) {
- return detailComponentManager.isDetailsVisible(itemId);
- }
-
- private static SelectionMode getDefaultSelectionMode() {
- return SelectionMode.SINGLE;
- }
-
- @Override
- public void readDesign(Element design, DesignContext context) {
- super.readDesign(design, context);
-
- Attributes attrs = design.attributes();
- if (attrs.hasKey("editable")) {
- setEditorEnabled(DesignAttributeHandler.readAttribute("editable",
- attrs, boolean.class));
- }
- if (attrs.hasKey("rows")) {
- setHeightByRows(DesignAttributeHandler.readAttribute("rows", attrs,
- double.class));
- setHeightMode(HeightMode.ROW);
- }
- if (attrs.hasKey("selection-mode")) {
- setSelectionMode(DesignAttributeHandler.readAttribute(
- "selection-mode", attrs, SelectionMode.class));
- }
-
- if (design.children().size() > 0) {
- if (design.children().size() > 1
- || !design.child(0).tagName().equals("table")) {
- throw new DesignException(
- "Grid needs to have a table element as its only child");
- }
- Element table = design.child(0);
-
- Elements colgroups = table.getElementsByTag("colgroup");
- if (colgroups.size() != 1) {
- throw new DesignException(
- "Table element in declarative Grid needs to have a"
- + " colgroup defining the columns used in Grid");
- }
-
- int i = 0;
- for (Element col : colgroups.get(0).getElementsByTag("col")) {
- String propertyId = DesignAttributeHandler.readAttribute(
- "property-id", col.attributes(), "property-" + i,
- String.class);
- addColumn(propertyId, String.class).readDesign(col, context);
- ++i;
- }
-
- for (Element child : table.children()) {
- if (child.tagName().equals("thead")) {
- header.readDesign(child, context);
- } else if (child.tagName().equals("tbody")) {
- for (Element row : child.children()) {
- Elements cells = row.children();
- Object[] data = new String[cells.size()];
- for (int c = 0; c < cells.size(); ++c) {
- data[c] = cells.get(c).html();
- }
- addRow(data);
- }
-
- // Since inline data is used, set HTML renderer for columns
- for (Column c : getColumns()) {
- c.setRenderer(new HtmlRenderer());
- }
- } else if (child.tagName().equals("tfoot")) {
- footer.readDesign(child, context);
- }
- }
- }
-
- // Read frozen columns after columns are read.
- if (attrs.hasKey("frozen-columns")) {
- setFrozenColumnCount(DesignAttributeHandler
- .readAttribute("frozen-columns", attrs, int.class));
- }
- }
-
- @Override
- public void writeDesign(Element design, DesignContext context) {
- super.writeDesign(design, context);
-
- Attributes attrs = design.attributes();
- Grid def = context.getDefaultInstance(this);
-
- DesignAttributeHandler.writeAttribute("editable", attrs,
- isEditorEnabled(), def.isEditorEnabled(), boolean.class);
-
- DesignAttributeHandler.writeAttribute("frozen-columns", attrs,
- getFrozenColumnCount(), def.getFrozenColumnCount(), int.class);
-
- if (getHeightMode() == HeightMode.ROW) {
- DesignAttributeHandler.writeAttribute("rows", attrs,
- getHeightByRows(), def.getHeightByRows(), double.class);
- }
-
- SelectionMode selectionMode = null;
-
- if (selectionModel.getClass().equals(SingleSelectionModel.class)) {
- selectionMode = SelectionMode.SINGLE;
- } else if (selectionModel.getClass()
- .equals(MultiSelectionModel.class)) {
- selectionMode = SelectionMode.MULTI;
- } else if (selectionModel.getClass().equals(NoSelectionModel.class)) {
- selectionMode = SelectionMode.NONE;
- }
-
- assert selectionMode != null : "Unexpected selection model "
- + selectionModel.getClass().getName();
-
- DesignAttributeHandler.writeAttribute("selection-mode", attrs,
- selectionMode, getDefaultSelectionMode(), SelectionMode.class);
-
- if (columns.isEmpty()) {
- // Empty grid. Structure not needed.
- return;
- }
-
- // Do structure.
- Element tableElement = design.appendElement("table");
- Element colGroup = tableElement.appendElement("colgroup");
-
- List<Column> columnOrder = getColumns();
- for (int i = 0; i < columnOrder.size(); ++i) {
- Column column = columnOrder.get(i);
- Element colElement = colGroup.appendElement("col");
- column.writeDesign(colElement, context);
- }
-
- // Always write thead. Reads correctly when there no header rows
- header.writeDesign(tableElement.appendElement("thead"), context);
-
- if (context.shouldWriteData(this)) {
- Element bodyElement = tableElement.appendElement("tbody");
- for (Object itemId : datasource.getItemIds()) {
- Element tableRow = bodyElement.appendElement("tr");
- for (Column c : getColumns()) {
- Object value = datasource.getItem(itemId)
- .getItemProperty(c.getPropertyId()).getValue();
- tableRow.appendElement("td")
- .append((value != null ? DesignFormatter
- .encodeForTextNode(value.toString()) : ""));
- }
- }
- }
-
- if (footer.getRowCount() > 0) {
- footer.writeDesign(tableElement.appendElement("tfoot"), context);
- }
- }
-
- @Override
- protected Collection<String> getCustomAttributes() {
- Collection<String> result = super.getCustomAttributes();
- result.add("editor-enabled");
- result.add("editable");
- result.add("frozen-column-count");
- result.add("frozen-columns");
- result.add("height-by-rows");
- result.add("rows");
- result.add("selection-mode");
- result.add("header-visible");
- result.add("footer-visible");
- result.add("editor-error-handler");
- result.add("height-mode");
-
- return result;
- }
- }
|