i am trying to make a "game" in Flex similar to Shake&Fidget. I'm saving all the user data in a mysql database and I retrieve the data using ZendFramework (PHP).
I thought of saving all the user and character info I'm going to need into am AS class so I can use that data in every view.
The data recived from the database is correct. I was able to load it in my app labbels but everytime i changed views it had to ask it again, so i thought of making this classes in order to just ask once for the information.
I'll post here the files so it's all more clear.
ViewNavigatorAplication.mxml
<s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
firstView="views.RotrHomeView"
persistNavigatorState="true">
<fx:Script>
<![CDATA[
import flash.net.registerClassAlias;
import valueObjects.Character;
registerClassAlias("Character", valueObjects.Character);
]]>
</fx:Script>
The first View goes to the login screen, it works OK. So i'll go to the view that loads and "tries" to show the character data.
char_panel.mxml
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:usersservice1="services.usersservice1.*"
title="Character Sheet"
viewActivate="char_panel_viewActivateHandler()">
<fx:Script>
<![CDATA[
import mx.binding.utils.*;
import mx.events.FlexEvent;
import mx.rpc.events.ResultEvent;
import valueObjects.Character;
//[Bindable]protected var character:Character = new Character();
public function updateStats():void{
var str:int = parseInt(getGlobalStatsResult.lastResult.globalSTR) + parseInt(getCharacterStatsResult.lastResult.str);
var dex:int = parseInt(getGlobalStatsResult.lastResult.globalDEX) + parseInt(getCharacterStatsResult.lastResult.dex);
var intel:int = parseInt(getGlobalStatsResult.lastResult.globalINT) + parseInt(getCharacterStatsResult.lastResult.intel);
var cha:int = parseInt(getGlobalStatsResult.lastResult.globalCHA) + parseInt(getCharacterStatsResult.lastResult.cha);
var sta:int = parseInt(getGlobalStatsResult.lastResult.global_VIT) + parseInt(getCharacterStatsResult.lastResult.vit);
data.modStats(str,intel,cha,sta,dex)
data.showStats(lb_show_str,lb_show_dex,lb_show_int,lb_show_cha,lb_show_vit);
//character.showStats(lb_show_str,lb_show_dex,lb_show_int,lb_show_cha,lb_show_vit);
}
public function char_panel_viewActivateHandler():void{
if(!data){
data = new Character();
}
getCharacterStatsResult.token = usersService1.getCharacterStats("user01");
getGearListResult.addEventListener(ResultEvent.RESULT,onResult);
getGearListResult.token = usersService1.getGearList();
}
public function onStatsResult(event:ResultEvent):void{
if(getGlobalStatsResult.lastResult.globalSTR != null){
updateStats();
}
}
public function onResult(event:ResultEvent):void{
if(getGearListResult.lastResult[0].itemName != null){
getGlobalStatsResult.addEventListener(ResultEvent.RESULT, onStatsResult);
getGlobalStatsResult.token = usersService1.getGlobalStats("user01");
currentState = "Character";
}
}
]]>
</fx:Script>
<s:states>
<s:State name="Loading"/>
<s:State name="Character"/>
</s:states>
<fx:Declarations>
<s:CallResponder id="getCharacterStatsResult"/>
<s:CallResponder id="getGearListResult"/>
<s:CallResponder id="getGlobalStatsResult"/>
<usersservice1:UsersService1 id="usersService1"/>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Label id="lb_show_str" includeIn="Character" x="119" y="46" text=""/>
<s:BusyIndicator includeIn="Loading" left="172" right="171" top="148" bottom="424"
horizontalCenter="0" verticalCenter="-138"/>
<s:Label id="lb_show_int" includeIn="Character" x="119" y="90"/>
<s:Label id="lb_show_cha" includeIn="Character" x="119" y="139"/>
<s:Label id="lb_show_vit" includeIn="Character" x="119" y="191"/>
<s:Label id="lb_show_dex" includeIn="Character" x="119" y="243"/>
As for the AS classes:
Character.as
package valueObjects{
import mx.data.ChangedItems;
import mx.messaging.channels.StreamingAMFChannel;
import spark.components.Label;
import spark.skins.spark.StackedFormHeadingSkin;
[Bindable]
public class Character
{
private var _name:String;
private var _stats:FinalStats;
private var _gear:GearList;
public function Character()
{
this._stats = new FinalStats();
this._gear = new GearList();
}
public function modStats(str:int,intel:int,cha:int,sta:int,dex:int):void{
this._stats.modStr(str);
this._stats.modInt(intel);
this._stats.modCha(cha);
this._stats.modVit(sta);
this._stats.modDex(dex);
}
public function getStats():Array{
var aStats:Array;
aStats["str"]=this._stats.getStr();
aStats["int"]=this._stats.getInt();
aStats["cha"]=this._stats.getCha();
aStats["sta"]=this._stats.getVit();
aStats["dex"]=this._stats.getDex();
return aStats;
}
public function setName(charName:String):void{
this._name = charName;
}
public function getName():String{
return this._name;
}
public function showStats(lbSTR:Label, lbDEX:Label, lbINT:Label, lbCHA:Label, lbVIT:Label):void{
lbSTR.text = "" + this._stats.getStr();
lbDEX.text = "" + this._stats.getDex();
lbINT.text = "" + this._stats.getInt();
lbCHA.text = "" + this._stats.getCha();
lbVIT.text = "" + this._stats.getVit();
}
}}
FinalStats.as
package valueObjects{
public class FinalStats
{
private var str:int = 0;
private var intel:int = 0;
private var cha:int = 0;
private var sta:int = 0;
private var dex:int = 0;
public function FinalStats()
{
}
public function getStr():int{
return this.str;
}
public function modStr(x:int):void{
this.str+=x;
}
public function getDex():int{
return this.dex;
}
public function modDex(x:int):void{
this.dex+=x;
}
public function getInt():int{
return this.intel;
}
public function modInt(x:int):void{
this.intel+=x;
}
public function getCha():int{
return this.cha;
}
public function modCha(x:int):void{
this.cha+=x;
}
public function getVit():int{
return this.sta;
}
public function modVit(x:int):void{
this.sta+=x;
}
}
}
And the last one GearList.as
package valueObjects{
import mx.data.ChangedItems;
import mx.messaging.channels.StreamingAMFChannel;
import spark.components.Label;
import spark.skins.spark.StackedFormHeadingSkin;
public class GearList
{
private var _headID:String;
private var _shoulderID:String;
private var _chestID:String;
private var _bracersID:String;
private var _glovesID:String;
private var _pantsID:String;
private var _bootsID:String;
private var _main_handID:String;
private var _off_handID:String;
public function GearList()
{
}
public function showStats(lbHead:Label, lbShoulder:Label, lbChest:Label, lbBracer:Label, lbGlove:Label, lbPants:Label, lbBoots:Label, lbMainHand:Label, lbOffHand:Label):void{
lbHead.text = ""+this._headID;
lbShoulder.text = ""+this._shoulderID;
lbChest.text = ""+this._chestID;
lbBracer.text = ""+this._bracersID;
lbGlove.text = ""+this._glovesID;
lbPants.text = ""+this._pantsID;
lbBoots.text = ""+this._bootsID;
lbMainHand.text = ""+this._main_handID;
lbOffHand.text = ""+this._off_handID;
}
public function getOff_handID():String
{
return _off_handID;
}
public function setOff_handID(value:String):void
{
_off_handID = value;
}
public function getMain_handID():String
{
return _main_handID;
}
public function setMain_handID(value:String):void
{
_main_handID = value;
}
public function getBootsID():String
{
return _bootsID;
}
public function setBootsID(value:String):void
{
_bootsID = value;
}
public function getPantsID():String
{
return _pantsID;
}
public function setPantsID(value:String):void
{
_pantsID = value;
}
public function getGlovesID():String
{
return _glovesID;
}
public function setGlovesID(value:String):void
{
_glovesID = value;
}
public function getBracersID():String
{
return _bracersID;
}
public function setBracersID(value:String):void
{
_bracersID = value;
}
public function getChestID():String
{
return _chestID;
}
public function setChestID(value:String):void
{
_chestID = value;
}
public function getShoulderID():String
{
return _shoulderID;
}
public function setShoulderID(value:String):void
{
_shoulderID = value;
}
public function getHeadID():String
{
return _headID;
}
public function setHeadID(value:String):void
{
_headID = value;
}
}}
If you are still here you have all my respect :D
When i try it out, i get the following error. I've tried in char_panel.mxml using Character::modStats(...) instead of data.modStats i'll put the error log under this one.
Error log when using data.modStats(...)
TypeError: Error #1006: modStats is not a function.
at views::char_panel/updateStats()[C:\Users\Zebrah\Rotr\Rotr\src\views\char_panel.mxml:23]
at views::char_panel/onStatsResult()[C:\Users\Zebrah\Rotr\Rotr\src\views\char_panel.mxml:39]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.rpc::CallResponder/result()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\CallResponder.as:122]
at mx.rpc::AsyncToken/http://www.adobe.com/2006/flex/mx/internal::applyResult()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\AsyncToken.as:239]
at mx.rpc.events::ResultEvent/http://www.adobe.com/2006/flex/mx/internal::callTokenResponders()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\events\ResultEvent.as:207]
at mx.rpc::AbstractOperation/http://www.adobe.com/2006/flex/mx/internal::dispatchRpcEvent()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\AbstractOperation.as:244]
at mx.rpc::AbstractInvoker/http://www.adobe.com/2006/flex/mx/internal::resultHandler()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\AbstractInvoker.as:318]
at mx.rpc::Responder/result()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\Responder.as:56]
at mx.rpc::AsyncRequest/acknowledge()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\AsyncRequest.as:84]
at NetConnectionMessageResponder/resultHandler()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\messaging\channels\NetConnectionChannel.as:552]
at mx.messaging::MessageResponder/result()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\messaging\MessageResponder.as:235]
Error Log using Character::modStats(...)
Error: Error #1034: Type Coercion failed: cannot convert valueObjects::Character$ to Namespace.
at views::char_panel/updateStats()[C:\Users\Zebrah\Rotr\Rotr\src\views\char_panel.mxml:23]
at views::char_panel/onStatsResult()[C:\Users\Zebrah\Rotr\Rotr\src\views\char_panel.mxml:39]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.rpc::CallResponder/result()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\CallResponder.as:122]
at mx.rpc::AsyncToken/http://www.adobe.com/2006/flex/mx/internal::applyResult()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\AsyncToken.as:239]
at mx.rpc.events::ResultEvent/http://www.adobe.com/2006/flex/mx/internal::callTokenResponders()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\events\ResultEvent.as:207]
at mx.rpc::AbstractOperation/http://www.adobe.com/2006/flex/mx/internal::dispatchRpcEvent()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\AbstractOperation.as:244]
at mx.rpc::AbstractInvoker/http://www.adobe.com/2006/flex/mx/internal::resultHandler()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\AbstractInvoker.as:318]
at mx.rpc::Responder/result()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\Responder.as:56]
at mx.rpc::AsyncRequest/acknowledge()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\rpc\AsyncRequest.as:84]
at NetConnectionMessageResponder/resultHandler()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\messaging\channels\NetConnectionChannel.as:552]
at mx.messaging::MessageResponder/result()[E:\dev\4.5.1\frameworks\projects\rpc\src\mx\messaging\MessageResponder.as:235]
Thanks in advance to anyone that get so far in this help cry :D i'd apreciate any sugestions that you can make.
ok so the reason it doesn't work with data is that event though you said
data = new Character();
data is still actually an object (that now looks like a character). to make it work
var myChar:Character = Character(data); // cast it to a Character and it now knows the method
myChar.modStats()
the reason the other call didn't work is the way you called it:
Character::modStats(...)
This say look for a method called modStats in the namespace Character. Instead call this
character.modStats()
and it will work.
You Sir are my personal Hero :D, i've just tried it and it shows the data.
I changed the constructor for the character in Character.AS like this:
public function Character(obj:Object)
{
this._stats = new FinalStats();
this._gear = new GearList();
}
I'm not entirely sure if this is correct, but as you mentioned in your answer:
var myChar:Character = Character(data);
I supose i'll have to use that data object to initialice myChar...am i right? Well, later when i have the time i'll try to add new views and buttons to go through views and see if the data is there :D
Related
I have a WPF app and I'm trying to use MEF to load viewmodels and view.
I can't successfully load Views.
The code:
public interface IContent
{
void OnNavigatedFrom( );
void OnNavigatedTo( );
}
public interface IContentMetadata
{
string ViewUri { get; }
}
[MetadataAttribute]
public class ExtensionMetadataAttribute : ExportAttribute
{
public string ViewUri { get; private set; }
public ExtensionMetadataAttribute(string uri) : base(typeof(IContentMetadata))
{
this.ViewUri = uri;
}
}
class ViewContentLoader
{
[ImportMany]
public IEnumerable<ExportFactory<IContent, IContentMetadata>> ViewExports
{
get;
set;
}
public object GetView(string uri)
{
// Get the factory for the View.
var viewMapping = ViewExports.FirstOrDefault(o =>
o.Metadata.ViewUri == uri);
if (viewMapping == null)
throw new InvalidOperationException(
String.Format("Unable to navigate to: {0}. " +
"Could not locate the View.",
uri));
var viewFactory = viewMapping.CreateExport();
var view = viewFactory.Value;
return viewFactory;
}
}
I supposed to use this code like this:
1)Decorate a User control
[Export(typeof(IContent))]
[ExtensionMetadata("CustomPause")]
[PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public partial class CustomPause : Page , IContent, IPartImportsSatisfiedNotification
{
public CustomPause()
{
InitializeComponent();
}
}
2) Compose the parts:
var cv = new CompositionContainer(aggregateCatalog);
var mef = new ViewContentLoader();
cv.ComposeParts(mef);
3) Load the view at runtime given a URI, for example:
private void CustomPause_Click(object sender, RoutedEventArgs e)
{
var vc = GlobalContainer.Instance.GetMefContainer() as ViewContentLoader;
MainWindow.MainFrame.Content = vc.GetView ("CustomPause");
}
Problem is this line in the GetView method fails:
var viewMapping = ViewExports.FirstOrDefault(o =>
o.Metadata.ViewUri == uri);
The query fails and so viewMapping is null but composition seems ok and I can see that ViewExports contains an object of type:
{System.ComponentModel.Composition.ExportFactory<EyesGuard.MEF.IContent, EyesGuard.MEF.IContentMetadata>[0]
I don't know where I'm wrong. Do you have a clue?
Gianpaolo
I had forgot this
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
in the MetadataAttribute
I am not having any issues making calls from the form instance to the Revit class. It's when I try to assign a List to the Revit class's function categoryList(), that I get a variable doesn't exist in the context error. I tried prefixing a reference to the instance of the form class "Form UF = new Form;" This doesn't work.
//The Revit Class
public Result Execute(ExternalCommandData commandData, ref string message,....)
{
User_Form UF = new User_Form(commandData);
UF.ShowDialog();
public List<BuiltInCategory> categoryList(Document doc, int intSwitch)
{
//list built in categories for built in filter_1
builtInCats_List = new List<BuiltInCategory>();
switch (intSwitch)
{
case (1):
...
case (3):
...
case (4):
{
builtInCats_List = newStateCb1;
return builtInCats_List;
}
default:
{
builtInCats_List = newStateCb1;
return builtInCats_List;
}
}
}
using Form = System.Windows.Forms.Form;
using WS = ModelAuditor_2014.WorksetSorter_2014;
using ModelAuditor_2014;
using System.Threading;
//The Form
namespace ModelAuditor_2014
{
public partial class User_Form : Form
{
//Constructor
WorksetSorter_2014 WS = new WorksetSorter_2014();
//Revit references
public Autodesk.Revit.UI.UIApplication rvtUiApp;
public Autodesk.Revit.UI.UIDocument rvtUiDoc;
public Autodesk.Revit.ApplicationServices.Application rvtApp;
//Global Variables
public List<BuiltInCategory> Filter01_CategoryList;
public List<BuiltInCategory> Filter02_CategoryList;
public int intSwitch;
public List<BuiltInCategory> newStateCb1;
public User_Form(ExternalCommandData commandData)
{
//Revit references
rvtUiApp = commandData.Application;
rvtUiDoc = rvtUiApp.ActiveUIDocument;
rvtApp = rvtUiApp.Application;
InitializeComponent();
}
public void User_Form_Load(object sender, EventArgs e)
{
//use rvtDoc = Doc
Autodesk.Revit.DB.Document rvtDoc = .....
//CheckedListBox for filter01
checkedListBox1.DataSource = WS.categoryList(rvtDoc, intSwitch = 1);
Filter01_CategoryList = new List<BuiltInCategory>();
Filter01_CategoryList = WS.RetrieveSchema(rvtDoc, false);
foreach (BuiltInCategory ChkedB1 in Filter01_CategoryList)
{
for (int i = 0; i < checkedListBox1.Items.Count; i++)
{
if (checkedListBox1.Items[i].ToString() == ChkedB1.ToString())
{
checkedListBox1.SetItemChecked(i, true);
}
}
}
public List<BuiltInCategory> returnNewStateCB1()
{
newStateCb1 = checkedListBox1.CheckedItems.Cast
<BuiltInCategory>().ToList<BuiltInCategory>();
return newStateCb1;
}
I passed the list from the win form to another public function in the revit app, I was able to access the list returned by this function.
I'm making a mp3/steaming radio with MVC, where I'm trying to load an URL from a array.
I have a radio class:
public class Radio
{
private var titel:String;
private var url:URLRequest;
private var cover:Bitmap;
public function Radio(titel:String, url:URLRequest, cover:Bitmap)
{
this.titel = titel;
this.url = url;
this.cover = cover;
}
public function getTitel():String {
return titel;
}
public function getURL():URLRequest {
return url;
}
public function getCover():Bitmap {
return cover
}
}
In the controller i have this:
public function selectRadio(radio:Radio):void{
model.selectRadio(radio);
}
In view I have the button with the eventlistner:
radio.addEventListener(MouseEvent.CLICK, function():void {
controller.selectRadio(model.getRadio(0));
});
And finally in the model i have:
private var radio:Radio = new Radio("P3", new URLRequest("http://live-icy.gss.dr.dk:80/A/A05L.mp3"), drp3);
private var radioArray:Array = new Array(radio);
private var r:Number;
public function selectRadio(radio:Radio):void {
var s:Sound = new Sound();
var chan:SoundChannel = s.play();
s.load();
trace("radio");
}
public function getRadios():Array {
return radioArray;
trace("All radio channels collected");
}
public function getRadio(radioNumber:int):Radio {
r = radioNumber;
return radioArray[radioNumber];
trace("Actual radio collected");
}
The problem is in the selectRadio function. I don't know how to load the URL in the arrays. It should be s.load(--something in here--); The reason why I'm doing this, is because I want to have multiple radio stations.
Hope you can help :)
var s:Sound = new Sound(radio.getURL());
var chan:SoundChannel = s.play();
load function will be called automatically by the constructor, also don't forget to stop previous SoundChannel.
and here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html you will find everything about Sound
Im trying to make an app that can dynamically load classes that implements an interface "IPlugin", i have:
var catalog = new AssemblyCatalog(typeof(Shell).Assembly);
var externalCatalog = new DirectoryCatalog(#".\Modules");
var container = new CompositionContainer(catalog);
var a = new AggregateCatalog(externalCatalog, catalog);
But when im trying to get the exports:
CompositionContainer __container = new CompositionContainer(a);
//get all the exports and load them into the appropriate list tagged with the importmany
__container.Compose(batch);
var yyyy = __container.GetExports<IModule>();
It doesnt find my "IPlugin" in the external assembly "Rejseplan".
Implementation of "Rejseplan" plugin:(the one that does not get loaded)
namespace Rejseplan
{
[ModuleExport(typeof(IPlugin), InitializationMode = InitializationMode.WhenAvailable)]
class RejseplanModule : IModule, IPlugin
{
private readonly IRegionViewRegistry regionViewRegistry;
[ImportingConstructor]
public RejseplanModule(IRegionViewRegistry registry)
{
this.regionViewRegistry = registry;
}
public void Initialize()
{
regionViewRegistry.RegisterViewWithRegion("MainRegion", typeof(Views.DepartureBoard));
}
string IPlugin.Name
{
get { throw new NotImplementedException(); }
}
string IPlugin.Version
{
get { throw new NotImplementedException(); }
}
string IPlugin.TabHeader
{
get { throw new NotImplementedException(); }
}
}
}
implmentation of "Test" plugin (the one that GETS loaded):
namespace HomeSystem
{
[Export(typeof(IPlugin))]
[ModuleExport(typeof(IModule), InitializationMode = InitializationMode.WhenAvailable)]
public class Test : IModule, IPlugin
{
public void Initialize()
{
}
public string Name
{
get { return "Test"; }
}
public string Version
{
get { return "Tis"; }
}
public string TabHeader
{
get { return "Tabt"; }
}
}
}
Hope you guys can helpCheers! :)
to be honest i dont really know what you wanna achieve :)
but if you want to see your RejseplanModule within your call:
__container.GetExports<IModule>();
you have to add the right Export attribute.
RejseplanModule is not MEF marked with Export of type IModule. can you check your code if its a typo or not? at least it should be the following (see the typeof(IModule))
EDIT:
external dll
[Export(typeof(IModule))]//<-- remove this if its handled by your custom ModulExport Attribute
[ModuleExport(typeof(IModule), InitializationMode = InitializationMode.WhenAvailable)]
class RejseplanModule : IModule, IPlugin
{...}
your main app(code from fhnaseer above)
var directoryPath = "path to dll folder";
var asmCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var directoryCatalog = new DirectoryCatalog(directoryPath, "*.dll");
var aggregateCatalog = new AggregateCatalog();
aggregateCatalog.Catalogs.Add(asmCatalog);
aggregateCatalog.Catalogs.Add(directoryCatalog);
var container = new CompositionContainer(aggregateCatalog);
var allIModulPlugins = container.GetExports<IModule>();
try to do this.
var directoryPath = "path to dll folder";
var asmCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var directoryCatalog = new DirectoryCatalog(directoryPath, "*.dll");
var aggregateCatalog = new AggregateCatalog();
aggregateCatalog.Catalogs.Add(asmCatalog);
aggregateCatalog.Catalogs.Add(directoryCatalog);
var container = new CompositionContainer(aggregateCatalog);
container.ComposeParts(this);
I've seen this kind of thing described in various examples showing how to create a REST service which takes arrays or a list of objects as part of the URL.
My question is, how to implement this using RESTeasy?
Something like the following would be how i would assume this to work.
#GET
#Path("/stuff/")
#Produces("application/json")
public StuffResponse getStuffByThings(
#QueryParam("things") List<Thing> things);
Create a StringConverter and a use a wrapper object. Here is a quick and dirty example:
public class QueryParamAsListTest {
public static class Thing {
String value;
Thing(String value){ this.value = value; }
}
public static class ManyThings {
List<Thing> things = new ArrayList<Thing>();
ManyThings(String values){
for(String value : values.split(",")){
things.add(new Thing(value));
}
}
}
static class Converter implements StringConverter<ManyThings> {
public ManyThings fromString(String str) {
return new ManyThings(str);
}
public String toString(ManyThings value) {
//TODO: implement
return value.toString();
}
}
#Path("/")
public static class Service {
#GET
#Path("/stuff/")
public int getStuffByThings(
#QueryParam("things") ManyThings things){
return things.things.size();
}
}
#Test
public void test() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getProviderFactory().addStringConverter(new Converter());
dispatcher.getRegistry().addSingletonResource(new Service());
MockHttpRequest request = MockHttpRequest.get("/stuff?things=a,b,c");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assert.assertEquals("3", response.getContentAsString());
}
}
I think you can also use a StringParamUnmarshaller
I had some luck with this, using Collection rather than List. I was unable to make a StringConverter for List work.
#Provider
public class CollectionConverter implements StringConverter<Collection<String>> {
public Collection<String> fromString(String string) {
if (string == null) {
return Collections.emptyList();
}
return Arrays.asList(string.split(","));
}
public String toString(Collection<String> values) {
final StringBuilder sb = new StringBuilder();
boolean first = true;
for (String value : values) {
if (first) {
first = false;
} else {
sb.append(",");
}
sb.append(value);
}
return sb.toString();
}
}
I did the toString from my head. Be sure to write unit tests for it to verify. But of course, everything is easier and clearer when you use Guava. Can use Joiner and Splitter. Really handy.
Just use a wrapper on its own, no need for anything else.
In your endpoint
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Path("/find")
#GET
MyResponse find(#QueryParam("ids") Wrapper ids);
And you wrapper looks like this :
public class Wrapper implements Serializable {
private List<BigInteger> ids = Collections.emptyList();
public String toString() {
return Joiner.on(",")
.join(ids);
}
public List<BigInteger> get() {
return ids;
}
public Wrapper(String s) {
if (s == null) {
ids = Collections.emptyList();
}
Iterable<String> splitted = Splitter.on(',')
.split(s);
Iterable<BigInteger> ids = Iterables.transform(splitted, Functionz.stringToBigInteger);
this.ids = Lists.newArrayList(ids);
}
public Wrapper(List<BigInteger> ids) {
this.ids = ids;
}
}