(()=>{var e,n,r,o={17816:function(t,e,n){var r=n(96763);t.exports=function(){"use strict";function t(){throw new Error("Dynamic requires are not currently supported by rollup-plugin-commonjs")}var e=function(t,e){return t(e={exports:{}},e.exports),e.exports}((function(e,n){var o;o=function(){return function e(n,r,o){function i(s,u){if(!r[s]){if(!n[s]){if(!u&&t)return t();if(a)return a(s,!0);var l=new Error("Cannot find module '"+s+"'");throw l.code="MODULE_NOT_FOUND",l}var c=r[s]={exports:{}};n[s][0].call(c.exports,(function(t){return i(n[s][1][t]||t)}),c,c.exports,e,n,r,o)}return r[s].exports}for(var a=t,s=0;s>>7-t%8&1)},put:function(t,e){for(var n=0;n>>e-n-1&1))},getLengthInBits:function(){return this.length},putBit:function(t){var e=Math.floor(this.length/8);this.buffer.length<=e&&this.buffer.push(0),t&&(this.buffer[e]|=128>>>this.length%8),this.length++}},e.exports=r},{}],5:[function(t,e,n){var r=t("../utils/buffer");function o(t){if(!t||t<1)throw new Error("BitMatrix size must be defined and greater than 0");this.size=t,this.data=r.alloc(t*t),this.reservedBit=r.alloc(t*t)}o.prototype.set=function(t,e,n,r){var o=t*this.size+e;this.data[o]=n,r&&(this.reservedBit[o]=!0)},o.prototype.get=function(t,e){return this.data[t*this.size+e]},o.prototype.xor=function(t,e,n){this.data[t*this.size+e]^=n},o.prototype.isReserved=function(t,e){return this.reservedBit[t*this.size+e]},e.exports=o},{"../utils/buffer":28}],6:[function(t,e,n){var r=t("../utils/buffer"),o=t("./mode");function i(t){this.mode=o.BYTE,this.data=r.from(t)}i.getBitsLength=function(t){return 8*t},i.prototype.getLength=function(){return this.data.length},i.prototype.getBitsLength=function(){return i.getBitsLength(this.data.length)},i.prototype.write=function(t){for(var e=0,n=this.data.length;e=0&&t.bit<4},n.from=function(t,e){if(n.isValid(t))return t;try{return function(t){if("string"!=typeof t)throw new Error("Param is not a string");switch(t.toLowerCase()){case"l":case"low":return n.L;case"m":case"medium":return n.M;case"q":case"quartile":return n.Q;case"h":case"high":return n.H;default:throw new Error("Unknown EC Level: "+t)}}(t)}catch(t){return e}}},{}],9:[function(t,e,n){var r=t("./utils").getSymbolSize;n.getPositions=function(t){var e=r(t);return[[0,0],[e-7,0],[0,e-7]]}},{"./utils":21}],10:[function(t,e,n){var r=t("./utils"),o=r.getBCHDigit(1335);n.getEncodedBits=function(t,e){for(var n=t.bit<<3|e,i=n<<10;r.getBCHDigit(i)-o>=0;)i^=1335<=33088&&n<=40956)n-=33088;else{if(!(n>=57408&&n<=60351))throw new Error("Invalid SJIS character: "+this.data[e]+"\nMake sure your charset is UTF-8");n-=49472}n=192*(n>>>8&255)+(255&n),t.put(n,13)}},e.exports=i},{"./mode":14,"./utils":21}],13:[function(t,e,n){n.Patterns={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7};var r=3,o=3,i=40,a=10;function s(t,e,r){switch(t){case n.Patterns.PATTERN000:return(e+r)%2==0;case n.Patterns.PATTERN001:return e%2==0;case n.Patterns.PATTERN010:return r%3==0;case n.Patterns.PATTERN011:return(e+r)%3==0;case n.Patterns.PATTERN100:return(Math.floor(e/2)+Math.floor(r/3))%2==0;case n.Patterns.PATTERN101:return e*r%2+e*r%3==0;case n.Patterns.PATTERN110:return(e*r%2+e*r%3)%2==0;case n.Patterns.PATTERN111:return(e*r%3+(e+r)%2)%2==0;default:throw new Error("bad maskPattern:"+t)}}n.isValid=function(t){return null!=t&&""!==t&&!isNaN(t)&&t>=0&&t<=7},n.from=function(t){return n.isValid(t)?parseInt(t,10):void 0},n.getPenaltyN1=function(t){for(var e=t.size,n=0,o=0,i=0,a=null,s=null,u=0;u=5&&(n+=r+(o-5)),a=c,o=1),(c=t.get(l,u))===s?i++:(i>=5&&(n+=r+(i-5)),s=c,i=1)}o>=5&&(n+=r+(o-5)),i>=5&&(n+=r+(i-5))}return n},n.getPenaltyN2=function(t){for(var e=t.size,n=0,r=0;r=10&&(1488===r||93===r)&&n++,o=o<<1&2047|t.get(s,a),s>=10&&(1488===o||93===o)&&n++}return n*i},n.getPenaltyN4=function(t){for(var e=0,n=t.data.length,r=0;r=1&&e<10?t.ccBits[0]:e<27?t.ccBits[1]:t.ccBits[2]},n.getBestModeForData=function(t){return o.testNumeric(t)?n.NUMERIC:o.testAlphanumeric(t)?n.ALPHANUMERIC:o.testKanji(t)?n.KANJI:n.BYTE},n.toString=function(t){if(t&&t.id)return t.id;throw new Error("Invalid mode")},n.isValid=function(t){return t&&t.bit&&t.ccBits},n.from=function(t,e){if(n.isValid(t))return t;try{return function(t){if("string"!=typeof t)throw new Error("Param is not a string");switch(t.toLowerCase()){case"numeric":return n.NUMERIC;case"alphanumeric":return n.ALPHANUMERIC;case"kanji":return n.KANJI;case"byte":return n.BYTE;default:throw new Error("Unknown mode: "+t)}}(t)}catch(t){return e}}},{"./regex":19,"./version-check":22}],15:[function(t,e,n){var r=t("./mode");function o(t){this.mode=r.NUMERIC,this.data=t.toString()}o.getBitsLength=function(t){return 10*Math.floor(t/3)+(t%3?t%3*3+1:0)},o.prototype.getLength=function(){return this.data.length},o.prototype.getBitsLength=function(){return o.getBitsLength(this.data.length)},o.prototype.write=function(t){var e,n,r;for(e=0;e+3<=this.data.length;e+=3)n=this.data.substr(e,3),r=parseInt(n,10),t.put(r,10);var o=this.data.length-e;o>0&&(n=this.data.substr(e),r=parseInt(n,10),t.put(r,3*o+1))},e.exports=o},{"./mode":14}],16:[function(t,e,n){var r=t("../utils/buffer"),o=t("./galois-field");n.mul=function(t,e){for(var n=r.alloc(t.length+e.length-1),i=0;i=0;){for(var i=n[0],a=0;a>r&1),r<6?t.set(r,8,o,!0):r<8?t.set(r+1,8,o,!0):t.set(i-15+r,8,o,!0),r<8?t.set(8,i-r-1,o,!0):r<9?t.set(8,15-r-1+1,o,!0):t.set(8,15-r-1,o,!0);t.set(i-8,8,1,!0)}function m(t,e,n){var i=new a;n.forEach((function(e){i.put(e.mode.bit,4),i.put(e.getLength(),g.getCharCountIndicator(e.mode,t)),e.write(i)}));var s=8*(o.getSymbolTotalCodewords(t)-f.getTotalCodewordsCount(t,e));for(i.getLengthInBits()+4<=s&&i.put(0,4);i.getLengthInBits()%8!=0;)i.putBit(0);for(var u=(s-i.getLengthInBits())/8,l=0;l=0&&s<=6&&(0===u||6===u)||u>=0&&u<=6&&(0===s||6===s)||s>=2&&s<=4&&u>=2&&u<=4?t.set(i+s,a+u,!0,!0):t.set(i+s,a+u,!1,!0))}(w,e),function(t){for(var e=t.size,n=8;n=7&&function(t,e){for(var n,r,o,i=t.size,a=p.getEncodedBits(e),s=0;s<18;s++)n=Math.floor(s/3),r=s%3+i-8-3,o=1==(a>>s&1),t.set(n,r,o,!0),t.set(r,n,o,!0)}(w,e),function(t,e){for(var n=t.size,r=-1,o=n-1,i=7,a=0,s=n-1;s>0;s-=2)for(6===s&&s--;;){for(var u=0;u<2;u++)if(!t.isReserved(o,s-u)){var l=!1;a>>i&1)),t.set(o,s-u,l),-1==--i&&(a++,i=7)}if((o+=r)<0||n<=o){o-=r,r=-r;break}}}(w,d),isNaN(r)&&(r=c.getBestMask(w,A.bind(null,w,n))),c.applyMask(r,w),A(w,n,r),{modules:w,version:e,errorCorrectionLevel:n,maskPattern:r,segments:i}}n.create=function(t,e){if(void 0===t||""===t)throw new Error("No input text");var n,r,a=i.M;return void 0!==e&&(a=i.from(e.errorCorrectionLevel,i.M),n=p.from(e.version),r=c.from(e.maskPattern),e.toSJISFunc&&o.setToSJISFunction(e.toSJISFunc)),w(t,n,a,r)}},{"../utils/buffer":28,"./alignment-pattern":2,"./bit-buffer":4,"./bit-matrix":5,"./error-correction-code":7,"./error-correction-level":8,"./finder-pattern":9,"./format-info":10,"./mask-pattern":13,"./mode":14,"./reed-solomon-encoder":18,"./segments":20,"./utils":21,"./version":23,isarray:33}],18:[function(t,e,n){var r=t("../utils/buffer"),o=t("./polynomial"),i=t("buffer").Buffer;function a(t){this.genPoly=void 0,this.degree=t,this.degree&&this.initialize(this.degree)}a.prototype.initialize=function(t){this.degree=t,this.genPoly=o.generateECPolynomial(this.degree)},a.prototype.encode=function(t){if(!this.genPoly)throw new Error("Encoder not initialized");var e=r.alloc(this.degree),n=i.concat([t,e],t.length+this.degree),a=o.mod(n,this.genPoly),s=this.degree-a.length;if(s>0){var u=r.alloc(this.degree);return a.copy(u,s),u}return a},e.exports=a},{"../utils/buffer":28,"./polynomial":16,buffer:30}],19:[function(t,e,n){var r="[0-9]+",o="(?:[u3000-u303F]|[u3040-u309F]|[u30A0-u30FF]|[uFF00-uFFEF]|[u4E00-u9FAF]|[u2605-u2606]|[u2190-u2195]|u203B|[u2010u2015u2018u2019u2025u2026u201Cu201Du2225u2260]|[u0391-u0451]|[u00A7u00A8u00B1u00B4u00D7u00F7])+",i="(?:(?![A-Z0-9 $%*+\\-./:]|"+(o=o.replace(/u/g,"\\u"))+")(?:.|[\r\n]))+";n.KANJI=new RegExp(o,"g"),n.BYTE_KANJI=new RegExp("[^A-Z0-9 $%*+\\-./:]+","g"),n.BYTE=new RegExp(i,"g"),n.NUMERIC=new RegExp(r,"g"),n.ALPHANUMERIC=new RegExp("[A-Z $%*+\\-./:]+","g");var a=new RegExp("^"+o+"$"),s=new RegExp("^"+r+"$"),u=new RegExp("^[A-Z0-9 $%*+\\-./:]+$");n.testKanji=function(t){return a.test(t)},n.testNumeric=function(t){return s.test(t)},n.testAlphanumeric=function(t){return u.test(t)}},{}],20:[function(t,e,n){var r=t("./mode"),o=t("./numeric-data"),i=t("./alphanumeric-data"),a=t("./byte-data"),s=t("./kanji-data"),u=t("./regex"),l=t("./utils"),c=t("dijkstrajs");function f(t){return unescape(encodeURIComponent(t)).length}function h(t,e,n){for(var r,o=[];null!==(r=t.exec(n));)o.push({data:r[0],index:r.index,mode:e,length:r[0].length});return o}function p(t){var e,n,o=h(u.NUMERIC,r.NUMERIC,t),i=h(u.ALPHANUMERIC,r.ALPHANUMERIC,t);return l.isKanjiModeEnabled()?(e=h(u.BYTE,r.BYTE,t),n=h(u.KANJI,r.KANJI,t)):(e=h(u.BYTE_KANJI,r.BYTE,t),n=[]),o.concat(i,e,n).sort((function(t,e){return t.index-e.index})).map((function(t){return{data:t.data,mode:t.mode,length:t.length}}))}function d(t,e){switch(e){case r.NUMERIC:return o.getBitsLength(t);case r.ALPHANUMERIC:return i.getBitsLength(t);case r.KANJI:return s.getBitsLength(t);case r.BYTE:return a.getBitsLength(t)}}function g(t,e){var n,u=r.getBestModeForData(t);if((n=r.from(e,u))!==r.BYTE&&n.bit=0?t[t.length-1]:null;return n&&n.mode===e.mode?(t[t.length-1].data+=e.data,t):(t.push(e),t)}),[]))},n.rawSplit=function(t){return n.fromArray(p(t,l.isKanjiModeEnabled()))}},{"./alphanumeric-data":3,"./byte-data":6,"./kanji-data":12,"./mode":14,"./numeric-data":15,"./regex":19,"./utils":21,dijkstrajs:31}],21:[function(t,e,n){var r,o=[0,26,44,70,100,134,172,196,242,292,346,404,466,532,581,655,733,815,901,991,1085,1156,1258,1364,1474,1588,1706,1828,1921,2051,2185,2323,2465,2611,2761,2876,3034,3196,3362,3532,3706];n.getSymbolSize=function(t){if(!t)throw new Error('"version" cannot be null or undefined');if(t<1||t>40)throw new Error('"version" should be in range from 1 to 40');return 4*t+17},n.getSymbolTotalCodewords=function(t){return o[t]},n.getBCHDigit=function(t){for(var e=0;0!==t;)e++,t>>>=1;return e},n.setToSJISFunction=function(t){if("function"!=typeof t)throw new Error('"toSJISFunc" is not a valid function.');r=t},n.isKanjiModeEnabled=function(){return void 0!==r},n.toSJIS=function(t){return r(t)}},{}],22:[function(t,e,n){n.isValid=function(t){return!isNaN(t)&&t>=1&&t<=40}},{}],23:[function(t,e,n){var r=t("./utils"),o=t("./error-correction-code"),i=t("./error-correction-level"),a=t("./mode"),s=t("./version-check"),u=t("isarray"),l=r.getBCHDigit(7973);function c(t,e){return a.getCharCountIndicator(t,e)+4}function f(t,e){var n=0;return t.forEach((function(t){var r=c(t.mode,e);n+=r+t.getBitsLength()})),n}n.from=function(t,e){return s.isValid(t)?parseInt(t,10):e},n.getCapacity=function(t,e,n){if(!s.isValid(t))throw new Error("Invalid QR Code version");void 0===n&&(n=a.BYTE);var i=8*(r.getSymbolTotalCodewords(t)-o.getTotalCodewordsCount(t,e));if(n===a.MIXED)return i;var u=i-c(n,t);switch(n){case a.NUMERIC:return Math.floor(u/10*3);case a.ALPHANUMERIC:return Math.floor(u/11*2);case a.KANJI:return Math.floor(u/13);case a.BYTE:default:return Math.floor(u/8)}},n.getBestVersionForData=function(t,e){var r,o=i.from(e,i.M);if(u(t)){if(t.length>1)return function(t,e){for(var r=1;r<=40;r++)if(f(t,r)<=n.getCapacity(r,e,a.MIXED))return r}(t,o);if(0===t.length)return 1;r=t[0]}else r=t;return function(t,e,r){for(var o=1;o<=40;o++)if(e<=n.getCapacity(o,r,t))return o}(r.mode,r.getLength(),o)},n.getEncodedBits=function(t){if(!s.isValid(t)||t<7)throw new Error("Invalid QR Code version");for(var e=t<<12;r.getBCHDigit(e)-l>=0;)e^=7973<':"",f="0&&l>0&&t[u-1]||(r+=a?i("M",l+n,.5+c+n):i("m",o,0),o=0,a=!1),l+1',h='viewBox="0 0 '+l+" "+l+'"',p=''+c+f+"\n";return"function"==typeof n&&n(null,p),p}},{"./utils":27}],27:[function(t,e,n){function r(t){if("number"==typeof t&&(t=t.toString()),"string"!=typeof t)throw new Error("Color should be defined as hex string");var e=t.slice().replace("#","").split("");if(e.length<3||5===e.length||e.length>8)throw new Error("Invalid hex color: "+t);3!==e.length&&4!==e.length||(e=Array.prototype.concat.apply([],e.map((function(t){return[t,t]})))),6===e.length&&e.push("F","F");var n=parseInt(e.join(""),16);return{r:n>>24&255,g:n>>16&255,b:n>>8&255,a:255&n,hex:"#"+e.slice(0,6).join("")}}n.getOptions=function(t){t||(t={}),t.color||(t.color={});var e=void 0===t.margin||null===t.margin||t.margin<0?4:t.margin,n=t.width&&t.width>=21?t.width:void 0,o=t.scale||4;return{width:n,scale:n?4:o,margin:e,color:{dark:r(t.color.dark||"#000000ff"),light:r(t.color.light||"#ffffffff")},type:t.type,rendererOpts:t.rendererOpts||{}}},n.getScale=function(t,e){return e.width&&e.width>=t+2*e.margin?e.width/(t+2*e.margin):e.scale},n.getImageWidth=function(t,e){var r=n.getScale(t,e);return Math.floor((t+2*e.margin)*r)},n.qrToImageData=function(t,e,r){for(var o=e.modules.size,i=e.modules.data,a=n.getScale(o,r),s=Math.floor((o+2*r.margin)*a),u=r.margin*a,l=[r.color.light,r.color.dark],c=0;c=u&&f>=u&&c=o)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+o.toString(16)+" bytes");return 0|t}function s(t,e){var n;return i.TYPED_ARRAY_SUPPORT?(n=new Uint8Array(e)).__proto__=i.prototype:(null===(n=t)&&(n=new i(e)),n.length=e),n}function u(t,e){var n=s(t,e<0?0:0|a(e));if(!i.TYPED_ARRAY_SUPPORT)for(var r=0;r55295&&n<57344){if(!o){if(n>56319){(e-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(e-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(e-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(e-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((e-=1)<0)break;i.push(n)}else if(n<2048){if((e-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((e-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function f(t){return i.isBuffer(t)?t.length:"undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(t)||t instanceof ArrayBuffer)?t.byteLength:("string"!=typeof t&&(t=""+t),0===t.length?0:c(t).length)}i.TYPED_ARRAY_SUPPORT&&(i.prototype.__proto__=Uint8Array.prototype,i.__proto__=Uint8Array,"undefined"!=typeof Symbol&&Symbol.species&&i[Symbol.species]===i&&Object.defineProperty(i,Symbol.species,{value:null,configurable:!0,enumerable:!1,writable:!1})),i.prototype.write=function(t,e,n){void 0===e||void 0===n&&"string"==typeof e?(n=this.length,e=0):isFinite(e)&&(e|=0,isFinite(n)?n|=0:n=void 0);var r=this.length-e;if((void 0===n||n>r)&&(n=r),t.length>0&&(n<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");return function(t,e,n,r){return function(t,e,n,r){for(var o=0;o=e.length||o>=t.length);++o)e[o+n]=t[o];return o}(c(e,t.length-n),t,n,r)}(this,t,e,n)},i.prototype.slice=function(t,e){var n,r=this.length;if((t=~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),(e=void 0===e?r:~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),e=t.length&&(e=t.length),e||(e=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),t.length-e=0;--o)t[o+e]=this[o+n];else if(a<1e3||!i.TYPED_ARRAY_SUPPORT)for(o=0;o>>=0,n=void 0===n?this.length:n>>>0,t||(t=0),"number"==typeof t)for(o=e;o0?a-4:a;for(n=0;n>16&255,l[c++]=e>>8&255,l[c++]=255&e;return 2===s&&(e=o[t.charCodeAt(n)]<<2|o[t.charCodeAt(n+1)]>>4,l[c++]=255&e),1===s&&(e=o[t.charCodeAt(n)]<<10|o[t.charCodeAt(n+1)]<<4|o[t.charCodeAt(n+2)]>>2,l[c++]=e>>8&255,l[c++]=255&e),l},n.fromByteArray=function(t){for(var e,n=t.length,o=n%3,i=[],a=16383,s=0,u=n-o;su?u:s+a));return 1===o?(e=t[n-1],i.push(r[e>>2]+r[e<<4&63]+"==")):2===o&&(e=(t[n-2]<<8)+t[n-1],i.push(r[e>>10]+r[e>>4&63]+r[e<<2&63]+"=")),i.join("")};for(var r=[],o=[],i="undefined"!=typeof Uint8Array?Uint8Array:Array,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=0;s<64;++s)r[s]=a[s],o[a.charCodeAt(s)]=s;function u(t){var e=t.length;if(e%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var n=t.indexOf("=");return-1===n&&(n=e),[n,n===e?0:4-n%4]}function l(t,e,n){for(var o,i=[],a=e;a>18&63]+r[s>>12&63]+r[s>>6&63]+r[63&s]);var s;return i.join("")}o["-".charCodeAt(0)]=62,o["_".charCodeAt(0)]=63},{}],30:[function(t,e,n){var o=t("base64-js"),i=t("ieee754"),a="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;n.Buffer=l,n.SlowBuffer=function(t){return+t!=t&&(t=0),l.alloc(+t)},n.INSPECT_MAX_BYTES=50;var s=2147483647;function u(t){if(t>s)throw new RangeError('The value "'+t+'" is invalid for option "size"');var e=new Uint8Array(t);return Object.setPrototypeOf(e,l.prototype),e}function l(t,e,n){if("number"==typeof t){if("string"==typeof e)throw new TypeError('The "string" argument must be of type string. Received type number');return h(t)}return c(t,e,n)}function c(t,e,n){if("string"==typeof t)return function(t,e){if("string"==typeof e&&""!==e||(e="utf8"),!l.isEncoding(e))throw new TypeError("Unknown encoding: "+e);var n=0|g(t,e),r=u(n),o=r.write(t,e);return o!==n&&(r=r.slice(0,o)),r}(t,e);if(ArrayBuffer.isView(t))return p(t);if(null==t)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);if(F(t,ArrayBuffer)||t&&F(t.buffer,ArrayBuffer))return function(t,e,n){if(e<0||t.byteLength=s)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+s.toString(16)+" bytes");return 0|t}function g(t,e){if(l.isBuffer(t))return t.length;if(ArrayBuffer.isView(t)||F(t,ArrayBuffer))return t.byteLength;if("string"!=typeof t)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof t);var n=t.length,r=arguments.length>2&&!0===arguments[2];if(!r&&0===n)return 0;for(var o=!1;;)switch(e){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":return D(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return j(t).length;default:if(o)return r?-1:D(t).length;e=(""+e).toLowerCase(),o=!0}}function v(t,e,n){var r=!1;if((void 0===e||e<0)&&(e=0),e>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(e>>>=0))return"";for(t||(t="utf8");;)switch(t){case"hex":return P(this,e,n);case"utf8":case"utf-8":return x(this,e,n);case"ascii":return N(this,e,n);case"latin1":case"binary":return M(this,e,n);case"base64":return _(this,e,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return L(this,e,n);default:if(r)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),r=!0}}function y(t,e,n){var r=t[e];t[e]=t[n],t[n]=r}function A(t,e,n,r,o){if(0===t.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),V(n=+n)&&(n=o?0:t.length-1),n<0&&(n=t.length+n),n>=t.length){if(o)return-1;n=t.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof e&&(e=l.from(e,r)),l.isBuffer(e))return 0===e.length?-1:m(t,e,n,r,o);if("number"==typeof e)return e&=255,"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(t,e,n):Uint8Array.prototype.lastIndexOf.call(t,e,n):m(t,[e],n,r,o);throw new TypeError("val must be string, number or Buffer")}function m(t,e,n,r,o){var i,a=1,s=t.length,u=e.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(t.length<2||e.length<2)return-1;a=2,s/=2,u/=2,n/=2}function l(t,e){return 1===a?t[e]:t.readUInt16BE(e*a)}if(o){var c=-1;for(i=n;is&&(n=s-u),i=n;i>=0;i--){for(var f=!0,h=0;ho&&(r=o):r=o;var i=e.length;r>i/2&&(r=i/2);for(var a=0;a>8,o=n%256,i.push(o),i.push(r);return i}(e,t.length-n),t,n,r)}function _(t,e,n){return 0===e&&n===t.length?o.fromByteArray(t):o.fromByteArray(t.slice(e,n))}function x(t,e,n){n=Math.min(t.length,n);for(var r=[],o=e;o239?4:l>223?3:l>191?2:1;if(o+f<=n)switch(f){case 1:l<128&&(c=l);break;case 2:128==(192&(i=t[o+1]))&&(u=(31&l)<<6|63&i)>127&&(c=u);break;case 3:i=t[o+1],a=t[o+2],128==(192&i)&&128==(192&a)&&(u=(15&l)<<12|(63&i)<<6|63&a)>2047&&(u<55296||u>57343)&&(c=u);break;case 4:i=t[o+1],a=t[o+2],s=t[o+3],128==(192&i)&&128==(192&a)&&128==(192&s)&&(u=(15&l)<<18|(63&i)<<12|(63&a)<<6|63&s)>65535&&u<1114112&&(c=u)}null===c?(c=65533,f=1):c>65535&&(c-=65536,r.push(c>>>10&1023|55296),c=56320|1023&c),r.push(c),o+=f}return function(t){var e=t.length;if(e<=B)return String.fromCharCode.apply(String,t);for(var n="",r=0;re&&(t+=" ... "),""},a&&(l.prototype[a]=l.prototype.inspect),l.prototype.compare=function(t,e,n,r,o){if(F(t,Uint8Array)&&(t=l.from(t,t.offset,t.byteLength)),!l.isBuffer(t))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof t);if(void 0===e&&(e=0),void 0===n&&(n=t?t.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),e<0||n>t.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&e>=n)return 0;if(r>=o)return-1;if(e>=n)return 1;if(this===t)return 0;for(var i=(o>>>=0)-(r>>>=0),a=(n>>>=0)-(e>>>=0),s=Math.min(i,a),u=this.slice(r,o),c=t.slice(e,n),f=0;f>>=0,isFinite(n)?(n>>>=0,void 0===r&&(r="utf8")):(r=n,n=void 0)}var o=this.length-e;if((void 0===n||n>o)&&(n=o),t.length>0&&(n<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return w(this,t,e,n);case"utf8":case"utf-8":return C(this,t,e,n);case"ascii":return b(this,t,e,n);case"latin1":case"binary":return k(this,t,e,n);case"base64":return E(this,t,e,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return T(this,t,e,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},l.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var B=4096;function N(t,e,n){var r="";n=Math.min(t.length,n);for(var o=e;or)&&(n=r);for(var o="",i=e;in)throw new RangeError("Trying to access beyond buffer length")}function R(t,e,n,r,o,i){if(!l.isBuffer(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>o||et.length)throw new RangeError("Index out of range")}function I(t,e,n,r,o,i){if(n+r>t.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function O(t,e,n,r,o){return e=+e,n>>>=0,o||I(t,0,n,4),i.write(t,e,n,r,23,4),n+4}function U(t,e,n,r,o){return e=+e,n>>>=0,o||I(t,0,n,8),i.write(t,e,n,r,52,8),n+8}l.prototype.slice=function(t,e){var n=this.length;(t=~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),(e=void 0===e?n:~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),e>>=0,e>>>=0,n||S(t,e,this.length);for(var r=this[t],o=1,i=0;++i>>=0,e>>>=0,n||S(t,e,this.length);for(var r=this[t+--e],o=1;e>0&&(o*=256);)r+=this[t+--e]*o;return r},l.prototype.readUInt8=function(t,e){return t>>>=0,e||S(t,1,this.length),this[t]},l.prototype.readUInt16LE=function(t,e){return t>>>=0,e||S(t,2,this.length),this[t]|this[t+1]<<8},l.prototype.readUInt16BE=function(t,e){return t>>>=0,e||S(t,2,this.length),this[t]<<8|this[t+1]},l.prototype.readUInt32LE=function(t,e){return t>>>=0,e||S(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},l.prototype.readUInt32BE=function(t,e){return t>>>=0,e||S(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},l.prototype.readIntLE=function(t,e,n){t>>>=0,e>>>=0,n||S(t,e,this.length);for(var r=this[t],o=1,i=0;++i=(o*=128)&&(r-=Math.pow(2,8*e)),r},l.prototype.readIntBE=function(t,e,n){t>>>=0,e>>>=0,n||S(t,e,this.length);for(var r=e,o=1,i=this[t+--r];r>0&&(o*=256);)i+=this[t+--r]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*e)),i},l.prototype.readInt8=function(t,e){return t>>>=0,e||S(t,1,this.length),128&this[t]?-1*(255-this[t]+1):this[t]},l.prototype.readInt16LE=function(t,e){t>>>=0,e||S(t,2,this.length);var n=this[t]|this[t+1]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt16BE=function(t,e){t>>>=0,e||S(t,2,this.length);var n=this[t+1]|this[t]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt32LE=function(t,e){return t>>>=0,e||S(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},l.prototype.readInt32BE=function(t,e){return t>>>=0,e||S(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},l.prototype.readFloatLE=function(t,e){return t>>>=0,e||S(t,4,this.length),i.read(this,t,!0,23,4)},l.prototype.readFloatBE=function(t,e){return t>>>=0,e||S(t,4,this.length),i.read(this,t,!1,23,4)},l.prototype.readDoubleLE=function(t,e){return t>>>=0,e||S(t,8,this.length),i.read(this,t,!0,52,8)},l.prototype.readDoubleBE=function(t,e){return t>>>=0,e||S(t,8,this.length),i.read(this,t,!1,52,8)},l.prototype.writeUIntLE=function(t,e,n,r){t=+t,e>>>=0,n>>>=0,r||R(this,t,e,n,Math.pow(2,8*n)-1,0);var o=1,i=0;for(this[e]=255&t;++i>>=0,n>>>=0,r||R(this,t,e,n,Math.pow(2,8*n)-1,0);var o=n-1,i=1;for(this[e+o]=255&t;--o>=0&&(i*=256);)this[e+o]=t/i&255;return e+n},l.prototype.writeUInt8=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,1,255,0),this[e]=255&t,e+1},l.prototype.writeUInt16LE=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,2,65535,0),this[e]=255&t,this[e+1]=t>>>8,e+2},l.prototype.writeUInt16BE=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,2,65535,0),this[e]=t>>>8,this[e+1]=255&t,e+2},l.prototype.writeUInt32LE=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,4,4294967295,0),this[e+3]=t>>>24,this[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t,e+4},l.prototype.writeUInt32BE=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,4,4294967295,0),this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t,e+4},l.prototype.writeIntLE=function(t,e,n,r){if(t=+t,e>>>=0,!r){var o=Math.pow(2,8*n-1);R(this,t,e,n,o-1,-o)}var i=0,a=1,s=0;for(this[e]=255&t;++i>>=0,!r){var o=Math.pow(2,8*n-1);R(this,t,e,n,o-1,-o)}var i=n-1,a=1,s=0;for(this[e+i]=255&t;--i>=0&&(a*=256);)t<0&&0===s&&0!==this[e+i+1]&&(s=1),this[e+i]=(t/a|0)-s&255;return e+n},l.prototype.writeInt8=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,1,127,-128),t<0&&(t=255+t+1),this[e]=255&t,e+1},l.prototype.writeInt16LE=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,2,32767,-32768),this[e]=255&t,this[e+1]=t>>>8,e+2},l.prototype.writeInt16BE=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,2,32767,-32768),this[e]=t>>>8,this[e+1]=255&t,e+2},l.prototype.writeInt32LE=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,4,2147483647,-2147483648),this[e]=255&t,this[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24,e+4},l.prototype.writeInt32BE=function(t,e,n){return t=+t,e>>>=0,n||R(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t,e+4},l.prototype.writeFloatLE=function(t,e,n){return O(this,t,e,!0,n)},l.prototype.writeFloatBE=function(t,e,n){return O(this,t,e,!1,n)},l.prototype.writeDoubleLE=function(t,e,n){return U(this,t,e,!0,n)},l.prototype.writeDoubleBE=function(t,e,n){return U(this,t,e,!1,n)},l.prototype.copy=function(t,e,n,r){if(!l.isBuffer(t))throw new TypeError("argument should be a Buffer");if(n||(n=0),r||0===r||(r=this.length),e>=t.length&&(e=t.length),e||(e=0),r>0&&r=this.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),t.length-e=0;--i)t[i+e]=this[i+n];else Uint8Array.prototype.set.call(t,this.subarray(n,r),e);return o},l.prototype.fill=function(t,e,n,r){if("string"==typeof t){if("string"==typeof e?(r=e,e=0,n=this.length):"string"==typeof n&&(r=n,n=this.length),void 0!==r&&"string"!=typeof r)throw new TypeError("encoding must be a string");if("string"==typeof r&&!l.isEncoding(r))throw new TypeError("Unknown encoding: "+r);if(1===t.length){var o=t.charCodeAt(0);("utf8"===r&&o<128||"latin1"===r)&&(t=o)}}else"number"==typeof t?t&=255:"boolean"==typeof t&&(t=Number(t));if(e<0||this.length>>=0,n=void 0===n?this.length:n>>>0,t||(t=0),"number"==typeof t)for(i=e;i55295&&n<57344){if(!o){if(n>56319){(e-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(e-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(e-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(e-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((e-=1)<0)break;i.push(n)}else if(n<2048){if((e-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((e-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function j(t){return o.toByteArray(function(t){if((t=(t=t.split("=")[0]).trim().replace(H,"")).length<2)return"";for(;t.length%4!=0;)t+="=";return t}(t))}function Y(t,e,n,r){for(var o=0;o=e.length||o>=t.length);++o)e[o+n]=t[o];return o}function F(t,e){return t instanceof e||null!=t&&null!=t.constructor&&null!=t.constructor.name&&t.constructor.name===e.name}function V(t){return t!=t}var z=function(){for(var t="0123456789abcdef",e=new Array(256),n=0;n<16;++n)for(var r=16*n,o=0;o<16;++o)e[r+o]=t[n]+t[o];return e}()},{"base64-js":29,ieee754:32}],31:[function(t,e,n){var r={single_source_shortest_paths:function(t,e,n){var o={},i={};i[e]=0;var a,s,u,l,c,f,h,p=r.PriorityQueue.make();for(p.push(e,0);!p.empty();)for(u in s=(a=p.pop()).value,l=a.cost,c=t[s]||{})c.hasOwnProperty(u)&&(f=l+c[u],h=i[u],(void 0===i[u]||h>f)&&(i[u]=f,p.push(u,f),o[u]=s));if(void 0!==n&&void 0===i[n]){var d=["Could not find a path from ",e," to ",n,"."].join("");throw new Error(d)}return o},extract_shortest_path_from_predecessor_list:function(t,e){for(var n=[],r=e;r;)n.push(r),t[r],r=t[r];return n.reverse(),n},find_path:function(t,e,n){var o=r.single_source_shortest_paths(t,e,n);return r.extract_shortest_path_from_predecessor_list(o,n)},PriorityQueue:{make:function(t){var e,n=r.PriorityQueue,o={};for(e in t=t||{},n)n.hasOwnPrope
/*
 * Copyright 2000-2014 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.server.widgetsetutils;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.vaadin.client.JsArrayObject;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.metadata.ConnectorBundleLoader;
import com.vaadin.client.metadata.ConnectorBundleLoader.CValUiInfo;
import com.vaadin.client.metadata.InvokationHandler;
import com.vaadin.client.metadata.OnStateChangeMethod;
import com.vaadin.client.metadata.ProxyHandler;
import com.vaadin.client.metadata.TypeData;
import com.vaadin.client.metadata.TypeDataStore;
import com.vaadin.client.ui.UnknownComponentConnector;
import com.vaadin.server.widgetsetutils.metadata.ClientRpcVisitor;
import com.vaadin.server.widgetsetutils.metadata.ConnectorBundle;
import com.vaadin.server.widgetsetutils.metadata.ConnectorInitVisitor;
import com.vaadin.server.widgetsetutils.metadata.GeneratedSerializer;
import com.vaadin.server.widgetsetutils.metadata.OnStateChangeVisitor;
import com.vaadin.server.widgetsetutils.metadata.Property;
import com.vaadin.server.widgetsetutils.metadata.ServerRpcVisitor;
import com.vaadin.server.widgetsetutils.metadata.StateInitVisitor;
import com.vaadin.server.widgetsetutils.metadata.TypeVisitor;
import com.vaadin.server.widgetsetutils.metadata.WidgetInitVisitor;
import com.vaadin.shared.annotations.Delayed;
import com.vaadin.shared.annotations.DelegateToWidget;
import com.vaadin.shared.communication.ClientRpc;
import com.vaadin.shared.communication.ServerRpc;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.Connect.LoadStyle;
import com.vaadin.tools.CvalAddonsChecker;
import com.vaadin.tools.CvalChecker;
import com.vaadin.tools.CvalChecker.InvalidCvalException;

public class ConnectorBundleLoaderFactory extends Generator {
    /**
     * Special SourceWriter that approximates the number of written bytes to
     * support splitting long methods into shorter chunks to avoid hitting the
     * 65535 byte limit.
     */
    private class SplittingSourceWriter implements SourceWriter {
        private final SourceWriter target;
        private final String baseName;
        private final int splitSize;
        private final List<String> methodNames;

        // Seems to be undercounted by about 15%
        private int approximateChars = 0;
        private int wrapCount = 0;

        public SplittingSourceWriter(SourceWriter target, String baseName,
                int splitSize) {
            this.target = target;
            this.baseName = baseName;
            this.splitSize = splitSize;
            methodNames = new ArrayList<String>();
            methodNames.add(baseName);
        }

        @Override
        public void beginJavaDocComment() {
            target.beginJavaDocComment();
            addChars(10);
        }

        private void addChars(int i) {
            approximateChars += i;
        }

        private void addChars(String s) {
            addChars(s.length());
        }

        private void addChars(String s, Object[] args) {
            addChars(String.format(s, args));
        }

        @Override
        public void commit(TreeLogger logger) {
            target.commit(logger);
        }

        @Override
        public void endJavaDocComment() {
            target.endJavaDocComment();
            addChars(10);
        }

        @Override
        public void indent() {
            target.indent();
            addChars(10);
        }

        @Override
        public void indentln(String s) {
            target.indentln(s);
            addChars(s);
        }

        @Override
        public void indentln(String s, Object... args) {
            target.indentln(s, args);
            addChars(s, args);
        }

        @Override
        public void outdent() {
            target.outdent();
        }

        @Override
        public void print(String s) {
            target.print(s);
            addChars(s);
        }

        @Override
        public void print(String s, Object... args) {
            target.print(s, args);
            addChars(s, args);
        }

        @Override
        public void println() {
            target.println();
            addChars(5);
        }

        @Override
        public void println(String s) {
            target.println(s);
            addChars(s);
        }

        @Override
        public void println(String s, Object... args) {
            target.println(s, args);
            addChars(s, args);
        }

        public void splitIfNeeded() {
            splitIfNeeded(false, null);
        }

        public void splitIfNeeded(boolean isNative, String params) {
            if (approximateChars > splitSize) {
                String newMethod = baseName + wrapCount++;
                String args = params == null ? "" : params;
                if (isNative) {
                    outdent();
                    println("}-*/;");
                    // To support fields of type long (#13692)
                    println("@com.google.gwt.core.client.UnsafeNativeLong");
                    println("private native void %s(%s) /*-{", newMethod, args);
                } else {
                    println("%s();", newMethod);
                    outdent();
                    println("}");
                    println("private void %s(%s) {", newMethod, args);
                }
                methodNames.add(newMethod);
                indent();

                approximateChars = 0;
            }
        }

        public List<String> getMethodNames() {
            return Collections.unmodifiableList(methodNames);
        }

    }

    private CvalAddonsChecker cvalChecker = new CvalAddonsChecker();

    @Override
    public String generate(TreeLogger logger, GeneratorContext context,
            String typeName) throws UnableToCompleteException {
        TypeOracle typeOracle = context.getTypeOracle();

        try {
            JClassType classType = typeOracle.getType(typeName);
            String packageName = classType.getPackage().getName();
            String className = classType.getSimpleSourceName() + "Impl";

            generateClass(logger, context, packageName, className, typeName);

            return packageName + "." + className;
        } catch (UnableToCompleteException e) {
            // Just rethrow
            throw e;
        } catch (Exception e) {
            logger.log(Type.ERROR, getClass() + " failed", e);
            throw new UnableToCompleteException();
        }
    }

    private void generateClass(TreeLogger logger, GeneratorContext context,
            String packageName, String className, String requestedType)
            throws Exception {
        PrintWriter printWriter = context.tryCreate(logger, packageName,
                className);
        if (printWriter == null) {
            return;
        }

        List<CValUiInfo> cvalInfos = null;
        try {
            if (cvalChecker != null) {
                cvalInfos = cvalChecker.run();
                // Don't run twice
                cvalChecker = null;
            }
        } catch (InvalidCvalException e) {
            System.err.println("\n\n\n\n" + CvalChecker.LINE);
            for (String line : e.getMessage().split("\n")) {
                System.err.println(line);
            }
            System.err.println(CvalChecker.LINE + "\n\n\n\n");
            System.exit(1);
            throw new UnableToCompleteException();
        }

        List<ConnectorBundle> bundles = buildBundles(logger,
                context.getTypeOracle());

        ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(
                packageName, className);
        composer.setSuperclass(requestedType);

        SourceWriter w = composer.createSourceWriter(context, printWriter);

        w.println("public void init() {");
        w.indent();

        for (ConnectorBundle bundle : bundles) {
            detectBadProperties(bundle, logger);

            String name = bundle.getName();
            boolean isEager = name
                    .equals(ConnectorBundleLoader.EAGER_BUNDLE_NAME);

            w.print("addAsyncBlockLoader(new AsyncBundleLoader(\"");
            w.print(escape(name));
            w.print("\", ");

            w.print("new String[] {");
            for (Entry<JClassType, Set<String>> entry : bundle.getIdentifiers()
                    .entrySet()) {
                Set<String> identifiers = entry.getValue();
                for (String id : identifiers) {
                    w.print("\"");
                    w.print(escape(id));
                    w.print("\",");
                }
            }
            w.println("}) {");
            w.indent();

            w.print("protected void load(final ");
            w.print(TypeDataStore.class.getName());
            w.println(" store) {");
            w.indent();

            if (!isEager) {
                w.print(GWT.class.getName());
                w.print(".runAsync(");
            }

            w.println("new %s() {", RunAsyncCallback.class.getName());
            w.indent();

            w.println("public void onSuccess() {");
            w.indent();

            w.println("load();");
            w.println("%s.get().setLoaded(getName());",
                    ConnectorBundleLoader.class.getName());

            // Close onSuccess method
            w.outdent();
            w.println("}");

            w.println("private void load() {");
            w.indent();

            String loadNativeJsBundle = "loadJsBundle";
            printBundleData(logger, w, bundle, loadNativeJsBundle);

            // Close load method
            w.outdent();
            w.println("}");

            // Separate method for loading native JS stuff (e.g. callbacks)
            String loadNativeJsMethodName = "loadNativeJs";
            // To support fields of type long (#13692)
            w.println("@com.google.gwt.core.client.UnsafeNativeLong");
            w.println("private native void %s(%s store) /*-{",
                    loadNativeJsMethodName, TypeDataStore.class.getName());
            w.indent();
            List<String> jsMethodNames = printJsBundleData(logger, w, bundle,
                    loadNativeJsMethodName);

            w.outdent();
            w.println("}-*/;");

            // Call all generated native method inside one Java method to avoid
            // refercences inside native methods to each other
            w.println("private void %s(%s store) {", loadNativeJsBundle,
                    TypeDataStore.class.getName());
            w.indent();
            printLoadJsBundleData(w, loadNativeJsBundle, jsMethodNames);
            w.outdent();
            w.println("}");

            // onFailure method declaration starts
            w.println("public void onFailure(Throwable reason) {");
            w.indent();

            w.println("%s.get().setLoadFailure(getName(), reason);",
                    ConnectorBundleLoader.class.getName());

            w.outdent();
            w.println("}");

            // Close new RunAsyncCallback() {}
            w.outdent();
            w.print("}");

            if (isEager) {
                w.println(".onSuccess();");
            } else {
                w.println(");");
            }

            // Close load method
            w.outdent();
            w.println("}");

            // Close add(new ...
            w.outdent();
            w.println("});");
        }

        if (cvalInfos != null && !cvalInfos.isEmpty()) {
            w.println("{");
            for (CValUiInfo c : cvalInfos) {
                if ("evaluation".equals(c.type)) {
                    w.println("cvals.add(new CValUiInfo(\"" + c.product
                            + "\", \"" + c.version + "\", \"" + c.widgetset
                            + "\", null));");
                }
            }
            w.println("}");
        }

        w.outdent();
        w.println("}");

        w.commit(logger);
    }

    private void printLoadJsBundleData(SourceWriter w, String methodName,
            List<String> methods) {
        SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName,
                30000);

        for (String method : methods) {
            writer.println("%s(store);", method);
            writer.splitIfNeeded();
        }
    }

    private void detectBadProperties(ConnectorBundle bundle, TreeLogger logger)
            throws UnableToCompleteException {
        Map<JClassType, Set<String>> definedProperties = new HashMap<JClassType, Set<String>>();

        for (Property property : bundle.getNeedsProperty()) {
            JClassType beanType = property.getBeanType();
            Set<String> usedPropertyNames = definedProperties.get(beanType);
            if (usedPropertyNames == null) {
                usedPropertyNames = new HashSet<String>();
                definedProperties.put(beanType, usedPropertyNames);
            }

            String name = property.getName();
            if (!usedPropertyNames.add(name)) {
                logger.log(Type.ERROR, beanType.getQualifiedSourceName()
                        + " has multiple properties with the name " + name
                        + ". This can happen if there are multiple "
                        + "setters with identical names ignoring case.");
                throw new UnableToCompleteException();
            }
            if (!property.hasAccessorMethods()) {
                logger.log(Type.ERROR, beanType.getQualifiedSourceName()
                        + " has the property '" + name
                        + "' without getter defined.");
                throw new UnableToCompleteException();
            }
        }
    }

    private List<String> printJsBundleData(TreeLogger logger, SourceWriter w,
            ConnectorBundle bundle, String methodName) {
        SplittingSourceWriter writer = new SplittingSourceWriter(w, methodName,
                30000);
        Set<Property> needsProperty = bundle.getNeedsProperty();
        for (Property property : needsProperty) {
            writer.println("var data = {");
            writer.indent();

            writer.println("setter: function(bean, value) {");
            writer.indent();
            property.writeSetterBody(logger, writer, "bean", "value");
            writer.outdent();
            writer.println("},");

            writer.println("getter: function(bean) {");
            writer.indent();
            property.writeGetterBody(logger, writer, "bean");
            writer.outdent();
            writer.println("}");

            writer.outdent();
            writer.println("};");

            // Method declaration
            writer.print(
                    "store.@%s::setPropertyData(Ljava/lang/Class;Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;)",
                    TypeDataStore.class.getName());
            writer.println("(@%s::class, '%s', data);", property.getBeanType()
                    .getQualifiedSourceName(), property.getName());
            writer.println();
            writer.splitIfNeeded(true,
                    String.format("%s store", TypeDataStore.class.getName()));
        }
        return writer.getMethodNames();
    }

    private void printBundleData(TreeLogger logger, SourceWriter sourceWriter,
            ConnectorBundle bundle, String loadNativeJsMethodName)
            throws UnableToCompleteException {
        // Split into new load method when reaching approximately 30000 bytes
        SplittingSourceWriter w = new SplittingSourceWriter(sourceWriter,
                "load", 30000);

        writeSuperClasses(w, bundle);
        writeIdentifiers(w, bundle);
        writeGwtConstructors(w, bundle);
        writeReturnTypes(w, bundle);
        writeInvokers(logger, w, bundle);
        writeParamTypes(w, bundle);
        writeProxys(w, bundle);
        writeDelayedInfo(w, bundle);

        w.println("%s(store);", loadNativeJsMethodName);

        // Must use Java code to generate Type data (because of Type[]), doing
        // this after the JS property data has been initialized
        writePropertyTypes(logger, w, bundle);
        writeSerializers(logger, w, bundle);
        writeDelegateToWidget(logger, w, bundle);
        writeOnStateChangeHandlers(logger, w, bundle);
    }

    private void writeOnStateChangeHandlers(TreeLogger logger,
            SplittingSourceWriter w, ConnectorBundle bundle)
            throws UnableToCompleteException {
        Map<JClassType, Set<JMethod>> needsOnStateChangeHandler = bundle
                .getNeedsOnStateChangeHandler();
        for (Entry<JClassType, Set<JMethod>> entry : needsOnStateChangeHandler
                .entrySet()) {
            JClassType connector = entry.getKey();

            TreeLogger typeLogger = logger.branch(
                    Type.DEBUG,
                    "Generating @OnStateChange support for "
                            + connector.getName());

            // Build map to speed up error checking
            HashMap<String, Property> stateProperties = new HashMap<String, Property>();
            JClassType stateType = ConnectorBundle
                    .findInheritedMethod(connector, "getState").getReturnType()
                    .isClassOrInterface();
            for (Property property : bundle.getProperties(stateType)) {
                stateProperties.put(property.getName(), property);
            }

            for (JMethod method : entry.getValue()) {
                TreeLogger methodLogger = typeLogger.branch(Type.DEBUG,
                        "Processing method " + method.getName());

                if (method.isPublic() || method.isProtected()) {
                    methodLogger
                            .log(Type.ERROR,
                                    "@OnStateChange is only supported for methods with private or default visibility.");
                    throw new UnableToCompleteException();
                }

                OnStateChange onStateChange = method
                        .getAnnotation(OnStateChange.class);

                String[] properties = onStateChange.value();

                if (properties.length == 0) {
                    methodLogger.log(Type.ERROR,
                            "There are no properties to listen to");
                    throw new UnableToCompleteException();
                }

                // Verify that all properties do exist
                for (String propertyName : properties) {
                    if (!stateProperties.containsKey(propertyName)) {
                        methodLogger.log(Type.ERROR,
                                "State class has no property named "
                                        + propertyName);
                        throw new UnableToCompleteException();
                    }
                }

                if (method.getParameters().length != 0) {
                    methodLogger.log(Type.ERROR,
                            "Method should accept zero parameters");
                    throw new UnableToCompleteException();
                }

                // new OnStateChangeMethod(Class declaringClass, String
                // methodName, String[], properties)
                w.print("store.addOnStateChangeMethod(%s, new %s(",
                        getClassLiteralString(connector),
                        OnStateChangeMethod.class.getName());
                if (!connector.equals(method.getEnclosingType())) {
                    w.print("%s, ",
                            getClassLiteralString(method.getEnclosingType()));
                }
                w.print("\"%s\", ", method.getName());

                w.print("new String[] {");
                for (String propertyName : properties) {
                    w.print("\"%s\", ", propertyName);
                }
                w.print("}");

                w.println("));");

                w.splitIfNeeded();
            }
        }
    }

    private void writeSuperClasses(SplittingSourceWriter w,
            ConnectorBundle bundle) {
        List<JClassType> needsSuperclass = new ArrayList<JClassType>(
                bundle.getNeedsSuperclass());
        // Emit in hierarchy order to ensure superclass is defined when
        // referenced
        Collections.sort(needsSuperclass, new Comparator<JClassType>() {

            @Override
            public int compare(JClassType type1, JClassType type2) {
                int depthDiff = getDepth(type1) - getDepth(type2);
                if (depthDiff != 0) {
                    return depthDiff;
                } else {
                    // Just something to get a stable compare
                    return type1.getName().compareTo(type2.getName());
                }
            }

            private int getDepth(JClassType type) {
                int depth = 0;
                while (type != null) {
                    depth++;
                    type = type.getSuperclass();
                }
                return depth;
            }
        });

        for (JClassType jClassType : needsSuperclass) {
            JClassType superclass = jClassType.getSuperclass();
            while (superclass != null && !superclass.isPublic()) {
                superclass = superclass.getSuperclass();
            }
            String classLiteralString;
            if (superclass == null) {
                classLiteralString = "null";
            } else {
                classLiteralString = getClassLiteralString(superclass);
            }
            w.println("store.setSuperClass(%s, %s);",
                    getClassLiteralString(jClassType), classLiteralString);
        }
    }

    private void writeDelegateToWidget(TreeLogger logger,
            SplittingSourceWriter w, ConnectorBundle bundle) {
        Map<JClassType, Set<Property>> needsDelegateToWidget = bundle
                .getNeedsDelegateToWidget();
        for (Entry<JClassType, Set<Property>> entry : needsDelegateToWidget
                .entrySet()) {
            JClassType beanType = entry.getKey();
            for (Property property : entry.getValue()) {
                w.println(
                        "store.setDelegateToWidget(%s, \"%s\", \"%s\");",
                        getClassLiteralString(beanType),// property.getBeanType()),
                        property.getName(),
                        property.getAnnotation(DelegateToWidget.class).value());
            }
            w.splitIfNeeded();
        }
    }

    private void writeSerializers(TreeLogger logger, SplittingSourceWriter w,
            ConnectorBundle bundle) throws UnableToCompleteException {
        Map<JType, GeneratedSerializer> serializers = bundle.getSerializers();
        for (Entry<JType, GeneratedSerializer> entry : serializers.entrySet()) {
            JType type = entry.getKey();
            GeneratedSerializer serializer = entry.getValue();

            w.print("store.setSerializerFactory(");
            writeClassLiteral(w, type);
            w.print(", ");
            w.println("new Invoker() {");
            w.indent();

            w.println("public Object invoke(Object target, Object[] params) {");
            w.indent();

            serializer.writeSerializerInstantiator(logger, w);

            w.outdent();
            w.println("}");

            w.outdent();
            w.print("}");
            w.println(");");

            w.splitIfNeeded();
        }
    }

    private void writePropertyTypes(TreeLogger logger, SplittingSourceWriter w,
            ConnectorBundle bundle) {
        Set<Property> properties = bundle.getNeedsProperty();
        for (Property property : properties) {
            w.print("store.setPropertyType(");
            writeClassLiteral(w, property.getBeanType());
            w.print(", \"");
            w.print(escape(property.getName()));
            w.print("\", ");
            writeTypeCreator(w, property.getPropertyType());
            w.println(");");

            w.splitIfNeeded();
        }
    }

    private void writeDelayedInfo(SplittingSourceWriter w,
            ConnectorBundle bundle) {
        Map<JClassType, Set<JMethod>> needsDelayedInfo = bundle
                .getNeedsDelayedInfo();
        Set<Entry<JClassType, Set<JMethod>>> entrySet = needsDelayedInfo
                .entrySet();
        for (Entry<JClassType, Set<JMethod>> entry : entrySet) {
            JClassType type = entry.getKey();
            Set<JMethod> methods = entry.getValue();
            for (JMethod method : methods) {
                Delayed annotation = method.getAnnotation(Delayed.class);
                if (annotation != null) {
                    w.print("store.setDelayed(");
                    writeClassLiteral(w, type);
                    w.print(", \"");
                    w.print(escape(method.getName()));
                    w.println("\");");

                    if (annotation.lastOnly()) {
                        w.print("store.setLastOnly(");
                        writeClassLiteral(w, type);
                        w.print(", \"");
                        w.print(escape(method.getName()));
                        w.println("\");");
                    }

                    w.splitIfNeeded();
                }
            }
        }
    }

    private void writeProxys(SplittingSourceWriter w, ConnectorBundle bundle) {
        Set<JClassType> needsProxySupport = bundle.getNeedsProxySupport();
        for (JClassType type : needsProxySupport) {
            w.print("store.setProxyHandler(");
            writeClassLiteral(w, type);
            w.print(", new ");
            w.print(ProxyHandler.class.getCanonicalName());
            w.println("() {");
            w.indent();

            w.println("public Object createProxy(final "
                    + InvokationHandler.class.getName() + " handler) {");
            w.indent();

            w.print("return new ");
            w.print(type.getQualifiedSourceName());
            w.println("() {");
            w.indent();

            JMethod[] methods = type.getOverridableMethods();
            for (JMethod method : methods) {
                if (method.isAbstract()) {
                    w.print("public ");
                    w.print(method.getReturnType().getQualifiedSourceName());
                    w.print(" ");
                    w.print(method.getName());
                    w.print("(");

                    JType[] types = method.getParameterTypes();
                    for (int i = 0; i < types.length; i++) {
                        if (i != 0) {
                            w.print(", ");
                        }
                        w.print(types[i].getQualifiedSourceName());
                        w.print(" p");
                        w.print(Integer.toString(i));
                    }

                    w.println(") {");
                    w.indent();

                    if (!method.getReturnType().getQualifiedSourceName()
                            .equals("void")) {
                        w.print("return ");
                    }

                    w.print("handler.invoke(this, ");
                    w.print(TypeData.class.getCanonicalName());
                    w.print(".getType(");
                    writeClassLiteral(w, type);
                    w.print(").getMethod(\"");
                    w.print(escape(method.getName()));
                    w.print("\"), new Object [] {");
                    for (int i = 0; i < types.length; i++) {
                        w.print("p" + i + ", ");
                    }
                    w.println("});");

                    w.outdent();
                    w.println("}");
                }
            }

            w.outdent();
            w.println("};");

            w.outdent();
            w.println("}");

            w.outdent();
            w.println("});");

            w.splitIfNeeded();
        }
    }

    private void writeParamTypes(SplittingSourceWriter w, ConnectorBundle bundle) {
        Map<JClassType, Set<JMethod>> needsParamTypes = bundle
                .getNeedsParamTypes();
        for (Entry<JClassType, Set<JMethod>> entry : needsParamTypes.entrySet()) {
            JClassType type = entry.getKey();

            Set<JMethod> methods = entry.getValue();
            for (JMethod method : methods) {
                w.print("store.setParamTypes(");
                writeClassLiteral(w, type);
                w.print(", \"");
                w.print(escape(method.getName()));
                w.print("\", new Type[] {");

                for (JType parameter : method.getParameterTypes()) {
                    ConnectorBundleLoaderFactory.writeTypeCreator(w, parameter);
                    w.print(", ");
                }

                w.println("});");

                w.splitIfNeeded();
            }
        }
    }

    private void writeInvokers(TreeLogger logger, SplittingSourceWriter w,
            ConnectorBundle bundle) throws UnableToCompleteException {
        Map<JClassType, Set<JMethod>> needsInvoker = bundle.getNeedsInvoker();
        for (Entry<JClassType, Set<JMethod>> entry : needsInvoker.entrySet()) {
            JClassType type = entry.getKey();

            TreeLogger typeLogger = logger.branch(Type.DEBUG,
                    "Creating invokers for " + type);

            Set<JMethod> methods = entry.getValue();
            for (JMethod method : methods) {
                w.print("store.setInvoker(");
                writeClassLiteral(w, type);
                w.print(", \"");
                w.print(escape(method.getName()));
                w.print("\",");

                if (method.isPublic()) {
                    typeLogger.log(Type.DEBUG, "Invoking " + method.getName()
                            + " using java");

                    writeJavaInvoker(w, type, method);
                } else {
                    TreeLogger methodLogger = typeLogger.branch(Type.DEBUG,
                            "Invoking " + method.getName() + " using jsni");
                    // Must use JSNI to access non-public methods
                    writeJsniInvoker(methodLogger, w, type, method);
                }

                w.println(");");

                w.splitIfNeeded();
            }
        }
    }

    private void writeJsniInvoker(TreeLogger logger, SplittingSourceWriter w,
            JClassType type, JMethod method) throws UnableToCompleteException {
        w.println("new JsniInvoker() {");
        w.indent();

        w.println(
                "protected native Object jsniInvoke(Object target, %s<Object> params) /*-{ ",
                JsArrayObject.class.getName());
        w.indent();

        JType returnType = method.getReturnType();
        boolean hasReturnType = !"void".equals(returnType
                .getQualifiedSourceName());

        // Note that void is also a primitive type
        boolean hasPrimitiveReturnType = hasReturnType
                && returnType.isPrimitive() != null;

        if (hasReturnType) {
            w.print("return ");

            if (hasPrimitiveReturnType) {
                // Integer.valueOf(expression);
                w.print("@%s::valueOf(%s)(", returnType.isPrimitive()
                        .getQualifiedBoxedSourceName(), returnType
                        .getJNISignature());

                // Implementation tested briefly, but I don't dare leave it
                // enabled since we are not using it in the framework and we
                // have not tests for it.
                logger.log(Type.ERROR,
                        "JSNI invocation is not yet supported for methods with "
                                + "primitive return types. Change your method "
                                + "to public to be able to use conventional"
                                + " Java invoking instead.");
                throw new UnableToCompleteException();
            }
        }

        JType[] parameterTypes = method.getParameterTypes();

        w.print("target.@%s::" + method.getName() + "(*)(", method
                .getEnclosingType().getQualifiedSourceName());
        for (int i = 0; i < parameterTypes.length; i++) {
            if (i != 0) {
                w.print(", ");
            }

            w.print("params[" + i + "]");

            JPrimitiveType primitive = parameterTypes[i].isPrimitive();
            if (primitive != null) {
                // param.intValue();
                w.print(".@%s::%sValue()()",
                        primitive.getQualifiedBoxedSourceName(),
                        primitive.getQualifiedSourceName());
            }
        }

        if (hasPrimitiveReturnType) {
            assert hasReturnType;
            w.print(")");
        }

        w.println(");");

        if (!hasReturnType) {
            w.println("return null;");
        }

        w.outdent();
        w.println("}-*/;");

        w.outdent();
        w.print("}");
    }

    private void writeJavaInvoker(SplittingSourceWriter w, JClassType type,
            JMethod method) {
        w.println("new Invoker() {");
        w.indent();

        w.println("public Object invoke(Object target, Object[] params) {");
        w.indent();

        JType returnType = method.getReturnType();
        boolean hasReturnType = !"void".equals(returnType
                .getQualifiedSourceName());
        if (hasReturnType) {
            w.print("return ");
        }

        JType[] parameterTypes = method.getParameterTypes();

        w.print("((" + type.getQualifiedSourceName() + ") target)."
                + method.getName() + "(");
        for (int i = 0; i < parameterTypes.length; i++) {
            JType parameterType = parameterTypes[i];
            if (i != 0) {
                w.print(", ");
            }
            String parameterTypeName = getBoxedTypeName(parameterType);
            w.print("(" + parameterTypeName + ") params[" + i + "]");
        }
        w.println(");");

        if (!hasReturnType) {
            w.println("return null;");
        }

        w.outdent();
        w.println("}");

        w.outdent();
        w.print("}");
    }

    private void writeReturnTypes(SplittingSourceWriter w,
            ConnectorBundle bundle) {
        Map<JClassType, Set<JMethod>> methodReturnTypes = bundle
                .getMethodReturnTypes();
        for (Entry<JClassType, Set<JMethod>> entry : methodReturnTypes
                .entrySet()) {
            JClassType type = entry.getKey();

            Set<JMethod> methods = entry.getValue();
            for (JMethod method : methods) {
                // setReturnType(Class<?> type, String methodName, Type
                // returnType)
                w.print("store.setReturnType(");
                writeClassLiteral(w, type);
                w.print(", \"");
                w.print(escape(method.getName()));
                w.print("\", ");
                writeTypeCreator(w, method.getReturnType());
                w.println(");");

                w.splitIfNeeded();
            }
        }
    }

    private void writeGwtConstructors(SplittingSourceWriter w,
            ConnectorBundle bundle) {
        Set<JClassType> constructors = bundle.getGwtConstructors();
        for (JClassType type : constructors) {
            w.print("store.setConstructor(");
            writeClassLiteral(w, type);
            w.println(", new Invoker() {");
            w.indent();

            w.println("public Object invoke(Object target, Object[] params) {");
            w.indent();

            w.print("return ");
            w.print(GWT.class.getName());
            w.print(".create(");
            writeClassLiteral(w, type);
            w.println(");");

            w.outdent();
            w.println("}");

            w.outdent();
            w.println("});");

            w.splitIfNeeded();
        }
    }

    public static void writeClassLiteral(SourceWriter w, JType type) {
        w.print(getClassLiteralString(type));
    }

    public static String getClassLiteralString(JType type) {
        return type.getQualifiedSourceName() + ".class";
    }

    private void writeIdentifiers(SplittingSourceWriter w,
            ConnectorBundle bundle) {
        Map<JClassType, Set<String>> identifiers = bundle.getIdentifiers();
        for (Entry<JClassType, Set<String>> entry : identifiers.entrySet()) {
            Set<String> ids = entry.getValue();
            JClassType type = entry.getKey();
            for (String id : ids) {
                w.print("store.setClass(\"");
                w.print(escape(id));
                w.print("\", ");
                writeClassLiteral(w, type);
                w.println(");");

                w.splitIfNeeded();
            }
        }
    }

    private List<ConnectorBundle> buildBundles(TreeLogger logger,
            TypeOracle typeOracle) throws NotFoundException,
            UnableToCompleteException {

        Map<LoadStyle, Collection<JClassType>> connectorsByLoadStyle = new HashMap<LoadStyle, Collection<JClassType>>();
        for (LoadStyle loadStyle : LoadStyle.values()) {
            connectorsByLoadStyle.put(loadStyle, new ArrayList<JClassType>());
        }

        // Find all types with a valid mapping
        Collection<JClassType> selectedTypes = getConnectorsForWidgetset(
                logger, typeOracle);

        // Group by load style
        for (JClassType connectorSubtype : selectedTypes) {
            LoadStyle loadStyle = getLoadStyle(connectorSubtype);
            if (loadStyle != null) {
                connectorsByLoadStyle.get(loadStyle).add(connectorSubtype);
            }
        }

        List<ConnectorBundle> bundles = new ArrayList<ConnectorBundle>();

        Collection<TypeVisitor> visitors = getVisitors(typeOracle);

        ConnectorBundle eagerBundle = new ConnectorBundle(
                ConnectorBundleLoader.EAGER_BUNDLE_NAME, visitors, typeOracle);
        TreeLogger eagerLogger = logger.branch(Type.TRACE,
                "Populating eager bundle");

        // Eager connectors and all RPC interfaces are loaded by default
        eagerBundle.processTypes(eagerLogger,
                connectorsByLoadStyle.get(LoadStyle.EAGER));
        eagerBundle.processType(eagerLogger, typeOracle
                .findType(UnknownComponentConnector.class.getCanonicalName()));
        eagerBundle.processSubTypes(eagerLogger,
                typeOracle.getType(ClientRpc.class.getName()));
        eagerBundle.processSubTypes(eagerLogger,
                typeOracle.getType(ServerRpc.class.getName()));

        bundles.add(eagerBundle);

        ConnectorBundle deferredBundle = new ConnectorBundle(
                ConnectorBundleLoader.DEFERRED_BUNDLE_NAME, eagerBundle);
        TreeLogger deferredLogger = logger.branch(Type.TRACE,
                "Populating deferred bundle");
        deferredBundle.processTypes(deferredLogger,
                connectorsByLoadStyle.get(LoadStyle.DEFERRED));

        bundles.add(deferredBundle);

        Collection<JClassType> lazy = connectorsByLoadStyle.get(LoadStyle.LAZY);
        for (JClassType type : lazy) {
            ConnectorBundle bundle = new ConnectorBundle(type.getName(),
                    eagerBundle);
            TreeLogger subLogger = logger.branch(Type.TRACE, "Populating "
                    + type.getName() + " bundle");
            bundle.processType(subLogger, type);

            bundles.add(bundle);
        }

        return bundles;
    }

    /**
     * Returns the connector types that should be included in the widgetset.
     * This method can be overridden to create a widgetset only containing
     * selected connectors.
     * <p>
     * The default implementation finds all type implementing
     * {@link ServerConnector} that have a @{@link Connect} annotation. It also
     * checks that multiple connectors aren't connected to the same server-side
     * class.
     *
     * @param logger
     *            the logger to which information can be logged
     * @param typeOracle
     *            the type oracle that can be used for finding types
     * @return a collection of all the connector types that should be included
     *         in the widgetset
     * @throws UnableToCompleteException
     *             if the operation fails
     */
    protected Collection<JClassType> getConnectorsForWidgetset(
            TreeLogger logger, TypeOracle typeOracle)
            throws UnableToCompleteException {
        JClassType serverConnectorType;
        try {
            serverConnectorType = typeOracle.getType(ServerConnector.class
                    .getName());
        } catch (NotFoundException e) {
            logger.log(Type.ERROR,
                    "Can't find " + ServerConnector.class.getName());
            throw new UnableToCompleteException();
        }

        JClassType[] types = serverConnectorType.getSubtypes();

        Map<String, JClassType> mappings = new HashMap<String, JClassType>();

        // Keep track of what has happened to avoid logging intermediate state
        Map<JClassType, List<JClassType>> replaced = new HashMap<JClassType, List<JClassType>>();

        for (JClassType type : types) {
            Connect connectAnnotation = type.getAnnotation(Connect.class);
            if (connectAnnotation == null) {
                continue;
            }

            String identifier = connectAnnotation.value().getCanonicalName();

            JClassType previousMapping = mappings.put(identifier, type);
            if (previousMapping != null) {
                // There are multiple mappings, pick the subclass
                JClassType subclass;
                JClassType superclass;
                if (previousMapping.isAssignableFrom(type)) {
                    subclass = type;
                    superclass = previousMapping;
                } else if (type.isAssignableFrom(previousMapping)) {
                    subclass = previousMapping;
                    superclass = type;
                } else {
                    // Neither inherits from the other - this is a conflict
                    logger.log(
                            Type.ERROR,
                            "Conflicting @Connect mappings detected for "
                                    + identifier
                                    + ": "
                                    + type.getQualifiedSourceName()
                                    + " and "
                                    + previousMapping.getQualifiedSourceName()
                                    + ". There can only be multiple @Connect mappings for the same server-side type if one is the subclass of the other.");
                    throw new UnableToCompleteException();
                }

                mappings.put(identifier, subclass);

                // Inherit any previous replacements
                List<JClassType> previousReplacements = replaced
                        .remove(superclass);
                if (previousReplacements == null) {
                    previousReplacements = new ArrayList<JClassType>();
                }

                previousReplacements.add(superclass);
                replaced.put(subclass, previousReplacements);
            }
        }

        // Log the final set of replacements
        for (Entry<JClassType, List<JClassType>> entry : replaced.entrySet()) {
            String msg = entry.getKey().getQualifiedSourceName() + " replaces ";

            List<JClassType> list = entry.getValue();
            for (int i = 0; i < list.size(); i++) {
                if (i != 0) {
                    msg += ", ";
                }
                msg += list.get(i).getQualifiedSourceName();
            }

            logger.log(Type.INFO, msg);
        }

        // Return the types of the final mapping
        return mappings.values();
    }

    private Collection<TypeVisitor> getVisitors(TypeOracle oracle)
            throws NotFoundException {
        List<TypeVisitor> visitors = Arrays.<TypeVisitor> asList(
                new ConnectorInitVisitor(), new StateInitVisitor(),
                new WidgetInitVisitor(), new ClientRpcVisitor(),
                new ServerRpcVisitor(), new OnStateChangeVisitor());
        for (TypeVisitor typeVisitor : visitors) {
            typeVisitor.init(oracle);
        }
        return visitors;
    }

    protected LoadStyle getLoadStyle(JClassType connectorType) {
        Connect annotation = connectorType.getAnnotation(Connect.class);
        return annotation.loadStyle();
    }

    public static String getBoxedTypeName(JType type) {
        if (type.isPrimitive() != null) {
            // Used boxed types for primitives
            return type.isPrimitive().getQualifiedBoxedSourceName();
        } else {
            return type.getErasedType().getQualifiedSourceName();
        }
    }

    public static void writeTypeCreator(SourceWriter sourceWriter, JType type) {
        String typeName = ConnectorBundleLoaderFactory.getBoxedTypeName(type);
        JParameterizedType parameterized = type.isParameterized();
        if (parameterized != null) {
            sourceWriter.print("new Type(\"" + typeName + "\", ");
            sourceWriter.print("new Type[] {");
            JClassType[] typeArgs = parameterized.getTypeArgs();
            for (JClassType jClassType : typeArgs) {
                writeTypeCreator(sourceWriter, jClassType);
                sourceWriter.print(", ");
            }
            sourceWriter.print("}");
        } else {
            sourceWriter.print("new Type(" + typeName + ".class");
        }
        sourceWriter.print(")");
    }

}