1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.mqtt;
17
18 import static io.netty.util.internal.ObjectUtil.checkPositive;
19
20 import io.netty.buffer.ByteBuf;
21 import io.netty.buffer.Unpooled;
22 import io.netty.handler.codec.mqtt.MqttProperties.MqttPropertyType;
23 import io.netty.util.CharsetUtil;
24
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28
29 public final class MqttMessageBuilders {
30
31 public static final class PublishBuilder {
32 private String topic;
33 private boolean retained;
34 private MqttQoS qos;
35 private ByteBuf payload;
36 private int messageId;
37 private MqttProperties mqttProperties;
38
39 PublishBuilder() {
40 }
41
42 public PublishBuilder topicName(String topic) {
43 this.topic = topic;
44 return this;
45 }
46
47 public PublishBuilder retained(boolean retained) {
48 this.retained = retained;
49 return this;
50 }
51
52 public PublishBuilder qos(MqttQoS qos) {
53 this.qos = qos;
54 return this;
55 }
56
57 public PublishBuilder payload(ByteBuf payload) {
58 this.payload = payload;
59 return this;
60 }
61
62 public PublishBuilder messageId(int messageId) {
63 this.messageId = messageId;
64 return this;
65 }
66
67 public PublishBuilder properties(MqttProperties properties) {
68 this.mqttProperties = properties;
69 return this;
70 }
71
72 public MqttPublishMessage build() {
73 MqttFixedHeader mqttFixedHeader = new MqttFixedHeader(MqttMessageType.PUBLISH, false, qos, retained, 0);
74 MqttPublishVariableHeader mqttVariableHeader =
75 new MqttPublishVariableHeader(topic, messageId, mqttProperties);
76 return new MqttPublishMessage(mqttFixedHeader, mqttVariableHeader, Unpooled.buffer().writeBytes(payload));
77 }
78 }
79
80 public static final class ConnectBuilder {
81
82 private MqttVersion version = MqttVersion.MQTT_3_1_1;
83 private String clientId;
84 private boolean cleanSession;
85 private boolean hasUser;
86 private boolean hasPassword;
87 private int keepAliveSecs;
88 private MqttProperties willProperties = MqttProperties.NO_PROPERTIES;
89 private boolean willFlag;
90 private boolean willRetain;
91 private MqttQoS willQos = MqttQoS.AT_MOST_ONCE;
92 private String willTopic;
93 private byte[] willMessage;
94 private String username;
95 private byte[] password;
96 private MqttProperties properties = MqttProperties.NO_PROPERTIES;
97
98 ConnectBuilder() {
99 }
100
101 public ConnectBuilder protocolVersion(MqttVersion version) {
102 this.version = version;
103 return this;
104 }
105
106 public ConnectBuilder clientId(String clientId) {
107 this.clientId = clientId;
108 return this;
109 }
110
111 public ConnectBuilder cleanSession(boolean cleanSession) {
112 this.cleanSession = cleanSession;
113 return this;
114 }
115
116 public ConnectBuilder keepAlive(int keepAliveSecs) {
117 this.keepAliveSecs = keepAliveSecs;
118 return this;
119 }
120
121 public ConnectBuilder willFlag(boolean willFlag) {
122 this.willFlag = willFlag;
123 return this;
124 }
125
126 public ConnectBuilder willQoS(MqttQoS willQos) {
127 this.willQos = willQos;
128 return this;
129 }
130
131 public ConnectBuilder willTopic(String willTopic) {
132 this.willTopic = willTopic;
133 return this;
134 }
135
136
137
138
139 @Deprecated
140 public ConnectBuilder willMessage(String willMessage) {
141 willMessage(willMessage == null ? null : willMessage.getBytes(CharsetUtil.UTF_8));
142 return this;
143 }
144
145 public ConnectBuilder willMessage(byte[] willMessage) {
146 this.willMessage = willMessage;
147 return this;
148 }
149
150 public ConnectBuilder willRetain(boolean willRetain) {
151 this.willRetain = willRetain;
152 return this;
153 }
154
155 public ConnectBuilder willProperties(MqttProperties willProperties) {
156 this.willProperties = willProperties;
157 return this;
158 }
159
160 public ConnectBuilder hasUser(boolean value) {
161 this.hasUser = value;
162 return this;
163 }
164
165 public ConnectBuilder hasPassword(boolean value) {
166 this.hasPassword = value;
167 return this;
168 }
169
170 public ConnectBuilder username(String username) {
171 this.hasUser = username != null;
172 this.username = username;
173 return this;
174 }
175
176
177
178
179 @Deprecated
180 public ConnectBuilder password(String password) {
181 password(password == null ? null : password.getBytes(CharsetUtil.UTF_8));
182 return this;
183 }
184
185 public ConnectBuilder password(byte[] password) {
186 this.hasPassword = password != null;
187 this.password = password;
188 return this;
189 }
190
191 public ConnectBuilder properties(MqttProperties properties) {
192 this.properties = properties;
193 return this;
194 }
195
196 public MqttConnectMessage build() {
197 MqttFixedHeader mqttFixedHeader =
198 new MqttFixedHeader(MqttMessageType.CONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
199 MqttConnectVariableHeader mqttConnectVariableHeader =
200 new MqttConnectVariableHeader(
201 version.protocolName(),
202 version.protocolLevel(),
203 hasUser,
204 hasPassword,
205 willRetain,
206 willQos.value(),
207 willFlag,
208 cleanSession,
209 keepAliveSecs,
210 properties);
211 MqttConnectPayload mqttConnectPayload =
212 new MqttConnectPayload(clientId, willProperties, willTopic, willMessage, username, password);
213 return new MqttConnectMessage(mqttFixedHeader, mqttConnectVariableHeader, mqttConnectPayload);
214 }
215 }
216
217 public static final class SubscribeBuilder {
218
219 private List<MqttTopicSubscription> subscriptions;
220 private int messageId;
221 private MqttProperties properties;
222
223 SubscribeBuilder() {
224 }
225
226 public SubscribeBuilder addSubscription(MqttQoS qos, String topic) {
227 ensureSubscriptionsExist();
228 subscriptions.add(new MqttTopicSubscription(topic, qos));
229 return this;
230 }
231
232 public SubscribeBuilder addSubscription(String topic, MqttSubscriptionOption option) {
233 ensureSubscriptionsExist();
234 subscriptions.add(new MqttTopicSubscription(topic, option));
235 return this;
236 }
237
238 public SubscribeBuilder messageId(int messageId) {
239 this.messageId = messageId;
240 return this;
241 }
242
243 public SubscribeBuilder properties(MqttProperties properties) {
244 this.properties = properties;
245 return this;
246 }
247
248 public MqttSubscribeMessage build() {
249 MqttFixedHeader mqttFixedHeader =
250 new MqttFixedHeader(MqttMessageType.SUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
251 MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader =
252 new MqttMessageIdAndPropertiesVariableHeader(messageId, properties);
253 MqttSubscribePayload mqttSubscribePayload = new MqttSubscribePayload(subscriptions);
254 return new MqttSubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
255 }
256
257 private void ensureSubscriptionsExist() {
258 if (subscriptions == null) {
259 subscriptions = new ArrayList<MqttTopicSubscription>(5);
260 }
261 }
262 }
263
264 public static final class UnsubscribeBuilder {
265
266 private List<String> topicFilters;
267 private int messageId;
268 private MqttProperties properties;
269
270 UnsubscribeBuilder() {
271 }
272
273 public UnsubscribeBuilder addTopicFilter(String topic) {
274 if (topicFilters == null) {
275 topicFilters = new ArrayList<String>(5);
276 }
277 topicFilters.add(topic);
278 return this;
279 }
280
281 public UnsubscribeBuilder messageId(int messageId) {
282 this.messageId = messageId;
283 return this;
284 }
285
286 public UnsubscribeBuilder properties(MqttProperties properties) {
287 this.properties = properties;
288 return this;
289 }
290
291 public MqttUnsubscribeMessage build() {
292 MqttFixedHeader mqttFixedHeader =
293 new MqttFixedHeader(MqttMessageType.UNSUBSCRIBE, false, MqttQoS.AT_LEAST_ONCE, false, 0);
294 MqttMessageIdAndPropertiesVariableHeader mqttVariableHeader =
295 new MqttMessageIdAndPropertiesVariableHeader(messageId, properties);
296 MqttUnsubscribePayload mqttSubscribePayload = new MqttUnsubscribePayload(topicFilters);
297 return new MqttUnsubscribeMessage(mqttFixedHeader, mqttVariableHeader, mqttSubscribePayload);
298 }
299 }
300
301 public interface PropertiesInitializer<T> {
302 void apply(T builder);
303 }
304
305 public static final class ConnAckBuilder {
306
307 private MqttConnectReturnCode returnCode;
308 private boolean sessionPresent;
309 private MqttProperties properties = MqttProperties.NO_PROPERTIES;
310 private ConnAckPropertiesBuilder propsBuilder;
311
312 private ConnAckBuilder() {
313 }
314
315 public ConnAckBuilder returnCode(MqttConnectReturnCode returnCode) {
316 this.returnCode = returnCode;
317 return this;
318 }
319
320 public ConnAckBuilder sessionPresent(boolean sessionPresent) {
321 this.sessionPresent = sessionPresent;
322 return this;
323 }
324
325 public ConnAckBuilder properties(MqttProperties properties) {
326 this.properties = properties;
327 return this;
328 }
329
330 public ConnAckBuilder properties(PropertiesInitializer<ConnAckPropertiesBuilder> consumer) {
331 if (propsBuilder == null) {
332 propsBuilder = new ConnAckPropertiesBuilder();
333 }
334 consumer.apply(propsBuilder);
335 return this;
336 }
337
338 public MqttConnAckMessage build() {
339 if (propsBuilder != null) {
340 properties = propsBuilder.build();
341 }
342 MqttFixedHeader mqttFixedHeader =
343 new MqttFixedHeader(MqttMessageType.CONNACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
344 MqttConnAckVariableHeader mqttConnAckVariableHeader =
345 new MqttConnAckVariableHeader(returnCode, sessionPresent, properties);
346 return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader);
347 }
348 }
349
350 public static final class ConnAckPropertiesBuilder {
351 private String clientId;
352 private Long sessionExpiryInterval;
353 private int receiveMaximum;
354 private Byte maximumQos;
355 private boolean retain;
356 private Long maximumPacketSize;
357 private int topicAliasMaximum;
358 private String reasonString;
359 private final MqttProperties.UserProperties userProperties = new MqttProperties.UserProperties();
360 private Boolean wildcardSubscriptionAvailable;
361 private Boolean subscriptionIdentifiersAvailable;
362 private Boolean sharedSubscriptionAvailable;
363 private Integer serverKeepAlive;
364 private String responseInformation;
365 private String serverReference;
366 private String authenticationMethod;
367 private byte[] authenticationData;
368
369 public MqttProperties build() {
370 final MqttProperties props = new MqttProperties();
371 if (clientId != null) {
372 props.add(new MqttProperties.StringProperty(MqttPropertyType.ASSIGNED_CLIENT_IDENTIFIER.value(),
373 clientId));
374 }
375 if (sessionExpiryInterval != null) {
376 props.add(new MqttProperties.IntegerProperty(
377 MqttPropertyType.SESSION_EXPIRY_INTERVAL.value(), sessionExpiryInterval.intValue()));
378 }
379 if (receiveMaximum > 0) {
380 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.RECEIVE_MAXIMUM.value(), receiveMaximum));
381 }
382 if (maximumQos != null) {
383 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.MAXIMUM_QOS.value(),
384 maximumQos.intValue()));
385 }
386 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.RETAIN_AVAILABLE.value(), retain ? 1 : 0));
387 if (maximumPacketSize != null) {
388 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.MAXIMUM_PACKET_SIZE.value(),
389 maximumPacketSize.intValue()));
390 }
391 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.TOPIC_ALIAS_MAXIMUM.value(),
392 topicAliasMaximum));
393 if (reasonString != null) {
394 props.add(new MqttProperties.StringProperty(MqttPropertyType.REASON_STRING.value(), reasonString));
395 }
396 props.add(userProperties);
397 if (wildcardSubscriptionAvailable != null) {
398 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.WILDCARD_SUBSCRIPTION_AVAILABLE.value(),
399 wildcardSubscriptionAvailable ? 1 : 0));
400 }
401 if (subscriptionIdentifiersAvailable != null) {
402 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.SUBSCRIPTION_IDENTIFIER_AVAILABLE.value(),
403 subscriptionIdentifiersAvailable ? 1 : 0));
404 }
405 if (sharedSubscriptionAvailable != null) {
406 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.SHARED_SUBSCRIPTION_AVAILABLE.value(),
407 sharedSubscriptionAvailable ? 1 : 0));
408 }
409 if (serverKeepAlive != null) {
410 props.add(new MqttProperties.IntegerProperty(MqttPropertyType.SERVER_KEEP_ALIVE.value(),
411 serverKeepAlive));
412 }
413 if (responseInformation != null) {
414 props.add(new MqttProperties.StringProperty(MqttPropertyType.RESPONSE_INFORMATION.value(),
415 responseInformation));
416 }
417 if (serverReference != null) {
418 props.add(new MqttProperties.StringProperty(MqttPropertyType.SERVER_REFERENCE.value(),
419 serverReference));
420 }
421 if (authenticationMethod != null) {
422 props.add(new MqttProperties.StringProperty(MqttPropertyType.AUTHENTICATION_METHOD.value(),
423 authenticationMethod));
424 }
425 if (authenticationData != null) {
426 props.add(new MqttProperties.BinaryProperty(MqttPropertyType.AUTHENTICATION_DATA.value(),
427 authenticationData));
428 }
429
430 return props;
431 }
432
433 public ConnAckPropertiesBuilder sessionExpiryInterval(long seconds) {
434 this.sessionExpiryInterval = seconds;
435 return this;
436 }
437
438 public ConnAckPropertiesBuilder receiveMaximum(int value) {
439 this.receiveMaximum = checkPositive(value, "value");
440 return this;
441 }
442
443 public ConnAckPropertiesBuilder maximumQos(byte value) {
444 if (value != 0 && value != 1) {
445 throw new IllegalArgumentException("maximum QoS property could be 0 or 1");
446 }
447 this.maximumQos = value;
448 return this;
449 }
450
451 public ConnAckPropertiesBuilder retainAvailable(boolean retain) {
452 this.retain = retain;
453 return this;
454 }
455
456 public ConnAckPropertiesBuilder maximumPacketSize(long size) {
457 this.maximumPacketSize = checkPositive(size, "size");
458 return this;
459 }
460
461 public ConnAckPropertiesBuilder assignedClientId(String clientId) {
462 this.clientId = clientId;
463 return this;
464 }
465
466 public ConnAckPropertiesBuilder topicAliasMaximum(int value) {
467 this.topicAliasMaximum = value;
468 return this;
469 }
470
471 public ConnAckPropertiesBuilder reasonString(String reason) {
472 this.reasonString = reason;
473 return this;
474 }
475
476 public ConnAckPropertiesBuilder userProperty(String name, String value) {
477 userProperties.add(name, value);
478 return this;
479 }
480
481 public ConnAckPropertiesBuilder wildcardSubscriptionAvailable(boolean value) {
482 this.wildcardSubscriptionAvailable = value;
483 return this;
484 }
485
486 public ConnAckPropertiesBuilder subscriptionIdentifiersAvailable(boolean value) {
487 this.subscriptionIdentifiersAvailable = value;
488 return this;
489 }
490
491 public ConnAckPropertiesBuilder sharedSubscriptionAvailable(boolean value) {
492 this.sharedSubscriptionAvailable = value;
493 return this;
494 }
495
496 public ConnAckPropertiesBuilder serverKeepAlive(int seconds) {
497 this.serverKeepAlive = seconds;
498 return this;
499 }
500
501 public ConnAckPropertiesBuilder responseInformation(String value) {
502 this.responseInformation = value;
503 return this;
504 }
505
506 public ConnAckPropertiesBuilder serverReference(String host) {
507 this.serverReference = host;
508 return this;
509 }
510
511 public ConnAckPropertiesBuilder authenticationMethod(String methodName) {
512 this.authenticationMethod = methodName;
513 return this;
514 }
515
516 public ConnAckPropertiesBuilder authenticationData(byte[] rawData) {
517 this.authenticationData = rawData.clone();
518 return this;
519 }
520 }
521
522 public static final class PubAckBuilder {
523
524 private int packetId;
525 private byte reasonCode;
526 private MqttProperties properties;
527
528 PubAckBuilder() {
529 }
530
531 public PubAckBuilder reasonCode(byte reasonCode) {
532 this.reasonCode = reasonCode;
533 return this;
534 }
535
536 public PubAckBuilder packetId(int packetId) {
537 this.packetId = packetId;
538 return this;
539 }
540
541
542
543
544 @Deprecated
545 public PubAckBuilder packetId(short packetId) {
546 return packetId(packetId & 0xFFFF);
547 }
548
549 public PubAckBuilder properties(MqttProperties properties) {
550 this.properties = properties;
551 return this;
552 }
553
554 public MqttMessage build() {
555 MqttFixedHeader mqttFixedHeader =
556 new MqttFixedHeader(MqttMessageType.PUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
557 MqttPubReplyMessageVariableHeader mqttPubAckVariableHeader =
558 new MqttPubReplyMessageVariableHeader(packetId, reasonCode, properties);
559 return new MqttMessage(mqttFixedHeader, mqttPubAckVariableHeader);
560 }
561 }
562
563 public static final class SubAckBuilder {
564
565 private int packetId;
566 private MqttProperties properties;
567 private final List<MqttQoS> grantedQoses = new ArrayList<MqttQoS>();
568
569 SubAckBuilder() {
570 }
571
572 public SubAckBuilder packetId(int packetId) {
573 this.packetId = packetId;
574 return this;
575 }
576
577
578
579
580 @Deprecated
581 public SubAckBuilder packetId(short packetId) {
582 return packetId(packetId & 0xFFFF);
583 }
584
585 public SubAckBuilder properties(MqttProperties properties) {
586 this.properties = properties;
587 return this;
588 }
589
590 public SubAckBuilder addGrantedQos(MqttQoS qos) {
591 this.grantedQoses.add(qos);
592 return this;
593 }
594
595 public SubAckBuilder addGrantedQoses(MqttQoS... qoses) {
596 this.grantedQoses.addAll(Arrays.asList(qoses));
597 return this;
598 }
599
600 public MqttSubAckMessage build() {
601 MqttFixedHeader mqttFixedHeader =
602 new MqttFixedHeader(MqttMessageType.SUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
603 MqttMessageIdAndPropertiesVariableHeader mqttSubAckVariableHeader =
604 new MqttMessageIdAndPropertiesVariableHeader(packetId, properties);
605
606
607 int[] grantedQoses = new int[this.grantedQoses.size()];
608 int i = 0;
609 for (MqttQoS grantedQos : this.grantedQoses) {
610 grantedQoses[i++] = grantedQos.value();
611 }
612
613 MqttSubAckPayload subAckPayload = new MqttSubAckPayload(grantedQoses);
614 return new MqttSubAckMessage(mqttFixedHeader, mqttSubAckVariableHeader, subAckPayload);
615 }
616 }
617
618 public static final class UnsubAckBuilder {
619
620 private int packetId;
621 private MqttProperties properties;
622 private final List<Short> reasonCodes = new ArrayList<Short>();
623
624 UnsubAckBuilder() {
625 }
626
627 public UnsubAckBuilder packetId(int packetId) {
628 this.packetId = packetId;
629 return this;
630 }
631
632
633
634
635 @Deprecated
636 public UnsubAckBuilder packetId(short packetId) {
637 return packetId(packetId & 0xFFFF);
638 }
639
640 public UnsubAckBuilder properties(MqttProperties properties) {
641 this.properties = properties;
642 return this;
643 }
644
645 public UnsubAckBuilder addReasonCode(short reasonCode) {
646 this.reasonCodes.add(reasonCode);
647 return this;
648 }
649
650 public UnsubAckBuilder addReasonCodes(Short... reasonCodes) {
651 this.reasonCodes.addAll(Arrays.asList(reasonCodes));
652 return this;
653 }
654
655 public MqttUnsubAckMessage build() {
656 MqttFixedHeader mqttFixedHeader =
657 new MqttFixedHeader(MqttMessageType.UNSUBACK, false, MqttQoS.AT_MOST_ONCE, false, 0);
658 MqttMessageIdAndPropertiesVariableHeader mqttSubAckVariableHeader =
659 new MqttMessageIdAndPropertiesVariableHeader(packetId, properties);
660
661 MqttUnsubAckPayload subAckPayload = new MqttUnsubAckPayload(reasonCodes);
662 return new MqttUnsubAckMessage(mqttFixedHeader, mqttSubAckVariableHeader, subAckPayload);
663 }
664 }
665
666 public static final class DisconnectBuilder {
667
668 private MqttProperties properties;
669 private byte reasonCode;
670
671 DisconnectBuilder() {
672 }
673
674 public DisconnectBuilder properties(MqttProperties properties) {
675 this.properties = properties;
676 return this;
677 }
678
679 public DisconnectBuilder reasonCode(byte reasonCode) {
680 this.reasonCode = reasonCode;
681 return this;
682 }
683
684 public MqttMessage build() {
685 MqttFixedHeader mqttFixedHeader =
686 new MqttFixedHeader(MqttMessageType.DISCONNECT, false, MqttQoS.AT_MOST_ONCE, false, 0);
687 MqttReasonCodeAndPropertiesVariableHeader mqttDisconnectVariableHeader =
688 new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties);
689
690 return new MqttMessage(mqttFixedHeader, mqttDisconnectVariableHeader);
691 }
692 }
693
694 public static final class AuthBuilder {
695
696 private MqttProperties properties;
697 private byte reasonCode;
698
699 AuthBuilder() {
700 }
701
702 public AuthBuilder properties(MqttProperties properties) {
703 this.properties = properties;
704 return this;
705 }
706
707 public AuthBuilder reasonCode(byte reasonCode) {
708 this.reasonCode = reasonCode;
709 return this;
710 }
711
712 public MqttMessage build() {
713 MqttFixedHeader mqttFixedHeader =
714 new MqttFixedHeader(MqttMessageType.AUTH, false, MqttQoS.AT_MOST_ONCE, false, 0);
715 MqttReasonCodeAndPropertiesVariableHeader mqttAuthVariableHeader =
716 new MqttReasonCodeAndPropertiesVariableHeader(reasonCode, properties);
717
718 return new MqttMessage(mqttFixedHeader, mqttAuthVariableHeader);
719 }
720 }
721
722 public static ConnectBuilder connect() {
723 return new ConnectBuilder();
724 }
725
726 public static ConnAckBuilder connAck() {
727 return new ConnAckBuilder();
728 }
729
730 public static PublishBuilder publish() {
731 return new PublishBuilder();
732 }
733
734 public static SubscribeBuilder subscribe() {
735 return new SubscribeBuilder();
736 }
737
738 public static UnsubscribeBuilder unsubscribe() {
739 return new UnsubscribeBuilder();
740 }
741
742 public static PubAckBuilder pubAck() {
743 return new PubAckBuilder();
744 }
745
746 public static SubAckBuilder subAck() {
747 return new SubAckBuilder();
748 }
749
750 public static UnsubAckBuilder unsubAck() {
751 return new UnsubAckBuilder();
752 }
753
754 public static DisconnectBuilder disconnect() {
755 return new DisconnectBuilder();
756 }
757
758 public static AuthBuilder auth() {
759 return new AuthBuilder();
760 }
761
762 private MqttMessageBuilders() {
763 }
764 }