1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.fourthline.cling.model.meta;
17
18 import org.fourthline.cling.model.ModelUtil;
19 import org.fourthline.cling.model.Validatable;
20 import org.fourthline.cling.model.ValidationError;
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.logging.Logger;
25
26
27
28
29
30
31 public class Action<S extends Service> implements Validatable {
32
33 final private static Logger log = Logger.getLogger(Action.class.getName());
34
35 final private String name;
36 final private ActionArgument[] arguments;
37 final private ActionArgument[] inputArguments;
38 final private ActionArgument[] outputArguments;
39
40
41 private S service;
42
43 public Action(String name, ActionArgument[] arguments) {
44 this.name = name;
45 if (arguments != null) {
46
47 List<ActionArgument> inputList= new ArrayList<>();
48 List<ActionArgument> outputList = new ArrayList<>();
49
50 for (ActionArgument argument : arguments) {
51 argument.setAction(this);
52 if (argument.getDirection().equals(ActionArgument.Direction.IN))
53 inputList.add(argument);
54 if (argument.getDirection().equals(ActionArgument.Direction.OUT))
55 outputList.add(argument);
56 }
57
58 this.arguments = arguments;
59 this.inputArguments = inputList.toArray(new ActionArgument[inputList.size()]);
60 this.outputArguments = outputList.toArray(new ActionArgument[outputList.size()]);
61 } else {
62 this.arguments = new ActionArgument[0];
63 this.inputArguments = new ActionArgument[0];
64 this.outputArguments = new ActionArgument[0];
65 }
66 }
67
68 public String getName() {
69 return name;
70 }
71
72 public boolean hasArguments() {
73 return getArguments() != null && getArguments().length > 0;
74 }
75
76 public ActionArgument[] getArguments() {
77 return arguments;
78 }
79
80 public S getService() {
81 return service;
82 }
83
84 void setService(S service) {
85 if (this.service != null)
86 throw new IllegalStateException("Final value has been set already, model is immutable");
87 this.service = service;
88 }
89
90 public ActionArgument<S> getFirstInputArgument() {
91 if (!hasInputArguments()) throw new IllegalStateException("No input arguments: " + this);
92 return getInputArguments()[0];
93 }
94
95 public ActionArgument<S> getFirstOutputArgument() {
96 if (!hasOutputArguments()) throw new IllegalStateException("No output arguments: " + this);
97 return getOutputArguments()[0];
98 }
99
100 public ActionArgument<S>[] getInputArguments() {
101 return inputArguments;
102 }
103
104 public ActionArgument<S> getInputArgument(String name) {
105 for (ActionArgument<S> arg : getInputArguments()) {
106 if (arg.isNameOrAlias(name)) return arg;
107 }
108 return null;
109 }
110
111 public ActionArgument<S>[] getOutputArguments() {
112 return outputArguments;
113 }
114
115 public ActionArgument<S> getOutputArgument(String name) {
116 for (ActionArgument<S> arg : getOutputArguments()) {
117 if (arg.getName().equals(name)) return arg;
118 }
119 return null;
120 }
121
122 public boolean hasInputArguments() {
123 return getInputArguments() != null && getInputArguments().length > 0;
124 }
125
126 public boolean hasOutputArguments() {
127 return getOutputArguments() != null && getOutputArguments().length > 0;
128 }
129
130
131 @Override
132 public String toString() {
133 return "(" + getClass().getSimpleName() +
134 ", Arguments: " + (getArguments() != null ? getArguments().length : "NO ARGS") +
135 ") " + getName();
136 }
137
138 public List<ValidationError> validate() {
139 List<ValidationError> errors = new ArrayList<>();
140
141 if (getName() == null || getName().length() == 0) {
142 errors.add(new ValidationError(
143 getClass(),
144 "name",
145 "Action without name of: " + getService()
146 ));
147 } else if (!ModelUtil.isValidUDAName(getName())) {
148 log.warning("UPnP specification violation of: " + getService().getDevice());
149 log.warning("Invalid action name: " + this);
150 }
151
152 for (ActionArgument actionArgument : getArguments()) {
153
154
155 if (getService().getStateVariable(actionArgument.getRelatedStateVariableName()) == null) {
156 errors.add(new ValidationError(
157 getClass(),
158 "arguments",
159 "Action argument references an unknown state variable: " + actionArgument.getRelatedStateVariableName()
160 ));
161 }
162 }
163
164 ActionArgument retValueArgument = null;
165 int retValueArgumentIndex = 0;
166 int i = 0;
167 for (ActionArgument actionArgument : getArguments()) {
168
169 if (actionArgument.isReturnValue()) {
170 if (actionArgument.getDirection() == ActionArgument.Direction.IN) {
171 log.warning("UPnP specification violation of :" + getService().getDevice());
172 log.warning("Input argument can not have <retval/>");
173 } else {
174 if (retValueArgument != null) {
175 log.warning("UPnP specification violation of: " + getService().getDevice());
176 log.warning("Only one argument of action '" + getName() + "' can be <retval/>");
177 }
178 retValueArgument = actionArgument;
179 retValueArgumentIndex = i;
180 }
181 }
182 i++;
183 }
184 if (retValueArgument != null) {
185 for (int j = 0; j < retValueArgumentIndex; j++) {
186 ActionArgument a = getArguments()[j];
187 if (a.getDirection() == ActionArgument.Direction.OUT) {
188 log.warning("UPnP specification violation of: " + getService().getDevice());
189 log.warning("Argument '" + retValueArgument.getName() + "' of action '" + getName() + "' is <retval/> but not the first OUT argument");
190 }
191 }
192 }
193
194 for (ActionArgument argument : arguments) {
195 errors.addAll(argument.validate());
196 }
197
198 return errors;
199 }
200
201 public Action<S> deepCopy() {
202 ActionArgument<S>[] actionArgumentsDupe = new ActionArgument[getArguments().length];
203 for (int i = 0; i < getArguments().length; i++) {
204 ActionArgument arg = getArguments()[i];
205 actionArgumentsDupe[i] = arg.deepCopy();
206 }
207
208 return new Action<>(
209 getName(),
210 actionArgumentsDupe
211 );
212 }
213
214 }