I have an empty interface called Data which is implemented by classes DataOne and DataTwo.
I then have a class called DataHolder which contains a Data object.
It looks something like this:
public class DataHolder() {
public Data data;
}
public class DataOne() {
public int importantData;
public int getImportantData() {
return importantData;
}
public int setImportantData(int importantData) {
this.importantData = importantData;
}
}
public class DataTwo() {
public int notSoImportantData;
}
Let's say theres a function which takes a DataHolder object and does some operation on the importantData integer.
public void calculateImportantData(DataHolder dh) {
int importantData = 1234567890;
dh.data.setImportantData(importantData);
}
How can I be sure that the DataHolder contains a DataOne object, without typecasting?
How about:
public class DataHolder<T extends Data> {
public T data;
}
and in your code you will have:
public void calculateImportantData(DataHolder<DataOne> dh) {
int importantData = 1234567890;
dh.data.setImportantData(importantData);
}
and I assume you meant DataOne and DataTwo to implement Data.
first of all , I tweaked your code a little bit ,
1- I created an Interface , Data , containing some random method someMethod() :
package main.interfaces;
public interface Data {
int myData = 0;
public void someMethod();
}
2- then , I created 2 classes called DataOne and DataTwo :
Class DataOne: ( notice how i added the important business method setImportantData() here , this provides total Encapsulation of your work).
package main;
import main.interfaces.Data;
public class DataOne implements Data{
public int importantData;
public int getImportantData() {
return importantData;
}
public void setImportantData(int importantData) {
this.importantData = importantData;
}
#Override
public void someMethod() {
System.out.println("here in DataOne!... ");
}
public void calculateImportantData(int importantData) {
// int importantData = 1234567890;
setImportantData(importantData);
}
}
Class DataTwo:
package main;
import main.interfaces.Data;
public class DataTwo implements Data{
public int notSoImportantData;
#Override
public void someMethod() {
System.out.println("here in DataTwo!...");
}
public void calculateUsualData(DataTwo d2) {
d2.someMethod();
}
}
after that , using Factory Design Pattern ... I created this DataFactory class:
package main.factory;
import main.DataOne;
import main.DataTwo;
import main.interfaces.Data;
public class DataFactory {
public static Data getData(String dataType){
if(dataType == null){
return null;
}
if(dataType.equalsIgnoreCase("DATAONE")){
return new DataOne();
} else if(dataType.equalsIgnoreCase("DATATWO")){
return new DataTwo();
}
return null;
}
}
now , back to your problem solution , I used DataHolder , encapsulating DataFactory here:
package main.holder;
import main.factory.DataFactory;
import main.interfaces.Data;
public class DataHolder {
Data data;
public DataHolder(String dataType){
data = DataFactory.getData(dataType);
}
public Data getData(){
return data;
}
}
now , try to run the application , I added some comments that will appear on your console , and I hope they will be helpful :)
package main.run;
import main.DataOne;
import main.DataTwo;
import main.holder.DataHolder;
import main.interfaces.Data;
public class main {
public static void main(String[] args) {
// lets assume user of the method passed a DataOne Object, you can
// manage it by changing the value of flag string
String flag = "DataOne";
DataHolder dataHolder = new DataHolder(flag);
if (dataHolder.getData() instanceof DataOne) {
System.out
.println("you have a DataOne object , but a Data reference");
System.out
.println("/nso , you need to create a 'reference' to DataOne to work on that object ...");
} else if (dataHolder.getData() instanceof DataTwo) {
System.out
.println("you have a DataTwo object , but a Data reference");
} else {
System.out
.println("you dont have a DataOne nor DataTwo references , it is a "
+ dataHolder.getData().getClass() + " object!");
}
System.out
.println("in order for the compiler to pass the following test , you must cast he RHS ( right hand side ) to match the LHS (left hand side)");
// in order for the compiler to pass the following test , you must cast
// the RHS ( right hand side ) to match the LHS (left hand side)
DataOne d1 = (DataOne) dataHolder.getData();
// in case you wanted to test DataTwo scenario
//DataTwo d2 = (DataTwo) dataHolder.getData();
System.out.println("if you didnt do that , you can make it a Data Object , but you will not be able to access the method 'getImportantData()' created in DataOne");
Data data = dataHolder.getData();
}
}
(note , here the program structure is : you select the type of the data before you start the application , stored in the "flag" variable inside the main method. after that , a call to DataHolder method will be made , after that , you can check the returned object and check if it is what u specified earlier. if you want it to be a little complicated , you can pass the object type in the DataHolder's constructor , and do the check from there , I didn't want to do it just for simplicity. Good Luck)
Related
Yes, I read many examples in web, but I didn't find a way how to call a method based on string value. May be I am not searching in right way... I wrote all code, but don't know how to call the method.
fyi: I don't want to use if else or switch case
Here is what I want:
I get the card reader type as String from database. I have to call the corresponding class' method.
My code:
LoginPanel.java
public class LoginPanel {
public static void main(String args[]) {
String readerType = "Omnikey5427-CK"; // I get this ("Omnikey5427-CK" or "Omnikey5427-G2") from a database as String
// I WANT TO CALL getCardNumber() method of respective class
}
}
ISmartCardReader.java
public interface ISmartCardReader {
public Integer getCardNumber();
}
Omnikey5427G2.java
public class Omnikey5427G2 implements ISmartCardReader {
public Omnikey5427G2() {
System.out.println("G222222222222222...");
}
public Integer getCardNumber() {
return 222;
}
}
Omnikey5427CK.java
public class Omnikey5427CK implements ISmartCardReader {
public Omnikey5427CK() {
System.out.println("CKKKKKKKKKKKKKKK...");
}
public Integer getCardNumber() {
return 111;
}
}
SmacrtCardEnumFactory.java
public enum SmacrtCardEnumFactory {
OMNIKEY5427CK("Omnikey5427-CK") {
public ISmartCardReader geInstance() {
return new Omnikey5427CK();
}
},
OMNIKEY5427G2("Omnikey5427-G2") {
public ISmartCardReader geInstance() {
return new Omnikey5427G2();
}
};
private String cardReaderName;
private SmacrtCardEnumFactory(String cardReaderName) {
this.cardReaderName = cardReaderName;
}
public String cardReaderName() {
return cardReaderName;
}
}
You can use valueOf() function of enum provided your enum sonstant names match strings used to lookup (you may use cardName.toUpper()). You may also create objects for all the card types and store them in a hash map and then lookup them. You can also write some fatory method, but this will be if-then-else or switch inside
You could iterate over the factory's values() and get the one that matches the string:
public enum SmacrtCardEnumFactory {
// current code omitted for brevity
public static getSmartCardReader(String name) {
return Arrays.stream(values())
.filter(r -> r.cardReaderName().equals(name))
.map(SmacrtCardEnumFactory::getInstance();
.orElse(null);
}
}
This is a continuation from what I was working in Passing 1 to many parameters of same object type
I've gotten good feedback on that , I believe i have the improved the design . The whole code is at https://github.com/spakai/flow_input_builder
The requirement is simple : -
I need to build a set of input for different workflows using 1 or more outputs from previous workflows
I have a set of interfaces
public interface SwfInput {
}
public interface SwfOutput {
}
public interface Workflow<I extends SwfInput, O extends SwfOutput> {
public O execute(I input);
}
public interface Builder<I extends SwfInput> {
public I build();
}
Now , Say I have 3 flows which gets executed in sequence FlowA->FlowB->FlowC
FlowC needs mandatory output from FlowB but only optionally from FlowA
so I have a implementation for FlowCBuilder
public class FlowCInputBuilder implements Builder<FlowCInput> {
private final FlowBOutput mandatoryflowBOutput;
private FlowAOutput optionalflowAOutput;
public FlowAOutput getOptionalflowAOutput() {
return optionalflowAOutput;
}
public FlowCInputBuilder setOptionalflowAOutput(FlowAOutput optionalflowAOutput) {
this.optionalflowAOutput = optionalflowAOutput;
return this;
}
public FlowCInputBuilder(FlowBOutput mandatoryflowBOutput) {
this.mandatoryflowBOutput = mandatoryflowBOutput;
}
#Override
public FlowCInput build() {
FlowCInput input = new FlowCInput();
input.setMandatoryFromFlowB(mandatoryflowBOutput.getOutput1FromB());
if (optionalflowAOutput != null) {
input.setOptionalFromFlowA(optionalflowAOutput.getOutput2FromA());
}
return input;
}
}
one test i have written shows an example usage
FlowBOutput mandatoryflowBOutput = new FlowBOutput();
mandatoryflowBOutput.setOutput1FromB("iNeedThis");
FlowAOutput optionalflowAOutput = new FlowAOutput();
FlowCInput input = new FlowCInputBuilder(mandatoryflowBOutput)
.setOptionalflowAOutput(optionalflowAOutput)
.build();
I have not used static inner class for the Builder pattern.
Any suggestions are welcomed.
You should use static inner class. The key point of using this approach is that, the inner can directly access private properties of the object being constructed. This helps eliminating duplicated code since the builder does not need to maintain a long list of temporary state for the constructing. So, your code can be rewritten like this:
public class FlowCInput {
private int output1FromB; // suppose that it is int
private String output2FromA; // suppose that it is String
private FlowCInput() { }
//...
public static class FlowCInputBuilder implements Builder<FlowCInput> {
private final FlowCInput result;
public FlowCInputBuilder(FlowBOutput mandatoryflowBOutput) {
result = new FlowCInput();
// output1FromB is private but still accessed from here
result.output1FromB = mandatoryflowBOutput.getOutput1FromB();
}
public FlowCInputBuilder setOptionalflowAOutput(FlowAOutput optionalflowAOutput) {
// same for output2FromA
result.output2FromA = optionalflowAOutput.getOutput2FromA();
return this;
}
#Override
public FlowCInput build() {
return result;
}
}
}
As you see, the builder now holds only a FlowCInput object, it does not unnecessarily hold mandatoryflowBOutput and optionalflowAOutput as before.
One.java
public class One {
String asd;
public class() {
asd="2d6"
}
public static void main(String args[]) {
Two a = new Two();
}
}
Two.java
public class Two {
ArrayList<String>data;
String asd;
public Two(String asd){
this.asd=asd;
data.add(this.asd);
}
}
How do I use this asd value of second for third class calling from first class's main method.
**Third class**
Per comments of #Maroun Maroun and #Bennyz, you can create a getter and setter method in your Two class:
import java.util.ArrayList;
public class Two {
ArrayList<String> data;
String asd;
public Two(String asd) {
this.asd = asd;
data = new ArrayList<>(); //<-- You needed to initialize the arraylist.
data.add(this.asd);
}
// Get value of 'asd',
public String getAsd() {
return asd;
}
// Set value of 'asd' to the argument given.
public void setAsd(String asd) {
this.asd = asd;
}
}
A great site to learn about this while coding (so not only reading), is CodeAcademy.
To use it in a third class, you can do this:
public class Third {
public static void main(String[] args) {
Two two = new Two("test");
String asd = two.getAsd(); //This hold now "test".
System.out.println("Value of asd: " + asd);
two.setAsd("something else"); //Set asd to "something else".
System.out.println(two.getAsd()); //Hey, it changed!
}
}
There are also some things not right about your code:
public class One {
String asd;
/**
* The name 'class' cannot be used for a method name, it is a reserved
* keyword.
* Also, this method is missing a return value.
* Last, you forgot a ";" after asd="2d6". */
public class() {
asd="2d6"
}
/** This is better. Best would be to create a setter method for this, or
* initialize 'asd' in your constructor. */
public void initializeAsd(){
asd = "2d6";
}
public static void main(String args[]) {
/**
* You haven't made a constructor without arguments.
* Either you make this in you Two class or use arguments in your call.
*/
Two a = new Two();
}
}
Per comment of #cricket_007, a better solution for the public class() method would be:
public class One {
String asd;
public One(){
asd = "2d6";
}
}
This way, when an One object is made (One one = new One), it has a asd field with "2d6" already.
The example below shows a class (Club) that contains a collection of an abstract class (Member). I'm confused as to whether I need a TypeAdapter or JsonDeserializer to make the Deserialization work correctly. Serialization works just fine without any help, but Deserialization is throwing exceptions. To illustrate I've built the following "clone" test. If anyone could show a working example I would be very grateful.
First Club Class
package gson.test;
import java.util.ArrayList;
import com.google.gson.Gson;
public class Club {
public static void main(String[] args) {
// Setup a Club with 2 members
Club myClub = new Club();
myClub.addMember(new Silver());
myClub.addMember(new Gold());
// Serialize to JSON
Gson gson = new Gson();
String myJsonClub = gson.toJson(myClub);
System.out.println(myJsonClub);
// De-Serialize to Club
Club myNewClub = gson.fromJson(myJsonClub, Club.class);
System.out.println(myClub.equals(myNewClub) ? "Cloned!" : "Failed");
}
private String title = "MyClub";
private ArrayList<Member> members = new ArrayList<Member>();
public boolean equals(Club that) {
if (!this.title.equals(that.title)) return false;
for (int i=0; i<this.members.size(); i++) {
if (! this.getMember(i).equals(that.getMember(i))) return false;
}
return true;
}
public void addMember(Member newMember) { members.add(newMember); }
public Member getMember(int i) { return members.get(i); }
}
Now the Abstract Base Class Member
package gson.test;
public abstract class Member {
private int type;
private String name = "";
public int getType() { return type; }
public void setType(int type) { this.type = type; }
public boolean equals(Member that) {return this.name.equals(that.name);}
}
And two concrete sub-classes of Member (Gold and Silver)
package gson.test;
public class Gold extends Member {
private String goldData = "SomeGoldData";
public Gold() {
super();
this.setType(2);
}
public boolean equals(Gold that) {
return (super.equals(that) && this.goldData.equals(that.goldData));
}
}
package gson.test;
public class Silver extends Member {
private String silverData = "SomeSilverData";
public Silver() {
super();
this.setType(1);
}
public boolean equals(Silver that) {
return (super.equals(that) && this.silverData.equals(that.silverData));
}
}
And finally the output
{"title":"MyClub","members":[{"silverData":"SomeSilverData","type":1,"name":""},{"goldData":"SomeGoldData","type":2,"name":""}]}
Exception in thread "main" java.lang.RuntimeException: Failed to invoke public gson.test.Member() with no args
at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:107)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:186)
...
You can do both. Which one you pick depends really on potential performance impact, and how much code are willing to write.
Deserializers are more expensive. That is because the input to deserializer is a json tree, and GSon will have to create a full JsonElement subtree for the element that matches your class, before it can pass it to your deserializer. If your model has a lot of nesting, that cost increases. For plain objects, it will be negligible.
It seems that you will know which class to create based on the value of type property that will be included in target object. Your deserializer will need to
look into the passed JsonElement object, read the type property, determine the type
call context.deserialize() with the class and the same element that was passed to you
throw an error if type was missing or invalid
Your type adapter will have to be more complex. The input to the type adapter is a stream, not an element/subtree. You can load the next value entirely from the stream, parse it, and then do exactly what deserializer did, which doesn't make sense and you can just use the deserializer instead. Alternatively, you can read the stream, see what properties there are, save them into local variables, until you get to the type property (you can't predict its location), then finish reading the remainder of the properties, and create your final Gold/Silver objects based on type, and all the properties read and saved.
Ok, real working example (I'm pretty sure this time).
The Club
package gson.test;
import java.util.ArrayList;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Club {
public static void main(String[] args) {
// Setup a Club with 2 members
Club myClub = new Club();
myClub.addMember(new Silver("Jack"));
myClub.addMember(new Gold("Jill"));
myClub.addMember(new Silver("Mike"));
// Get the GSON Object and register Type Adapter
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Member.class, new MemberDeserializer());
builder.registerTypeAdapter(Member.class, new MemberSerializer());
builder.setPrettyPrinting();
Gson gson = builder.create();
// Serialize Club to JSON
String myJsonClub = gson.toJson(myClub);
// De-Serialize to Club
Club myNewClub = gson.fromJson(myJsonClub, Club.class);
System.out.println(myClub.equals(myNewClub) ? "Cloned!" : "Failed");
System.out.println(gson.toJson(myNewClub));
}
private String title = "MyClub";
private ArrayList<Member> members = new ArrayList<Member>();
public boolean equals(Object club) {
Club that = (Club) club;
if (!this.title.equals(that.title)) return false;
for (int i=0; i<this.members.size(); i++) {
Member member1 = this.getMember(i);
Member member2 = that.getMember(i);
if (! member1.equals(member2)) return false;
}
return true;
}
public void addMember(Member newMember) { members.add(newMember); }
public Member getMember(int i) { return members.get(i); }
}
The Member Abstract Class
package gson.test;
public abstract class Member {
private String clsname = this.getClass().getName() ;
private int type;
private String name = "unknown";
public Member() { }
public Member(String theName) {this.name = theName;}
public int getType() { return type; }
public void setType(int type) { this.type = type; }
public boolean equals(Object member) {
Member that = (Member) member;
return this.name.equals(that.name);
}
}
The Concrete Sub-Classes Silver and Gold
package gson.test;
public class Silver extends Member {
private String silverData = "SomeSilverData";
public Silver() {
super();
this.setType(1);
}
public Silver(String theName) {
super(theName);
this.setType(1);
}
public boolean equals(Object that) {
Silver silver = (Silver)that;
return (super.equals(that) && this.silverData.equals(silver.silverData));
}
}
package gson.test;
public class Gold extends Member {
private String goldData = "SomeGoldData";
private String extraData = "Extra Gold Data";
public Gold() {
super();
this.setType(2);
}
public Gold(String theName) {
super(theName);
this.setType(2);
}
public boolean equals(Gold that) {
Gold gold = (Gold) that;
return (super.equals(that) && this.goldData.equals(gold.goldData));
}
}
The Custom Member Serailizer
package gson.test;
import java.lang.reflect.Type;
import com.google.gson.JsonElement;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
public class MemberSerializer implements JsonSerializer<Member> {
public JsonElement serialize(Member src, Type member, JsonSerializationContext context) {
switch (src.getType()) {
case 1: return context.serialize((Silver)src);
case 2: return context.serialize((Gold)src);
default: return null;
}
}
}
The custom Deserializer
package gson.test;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
public class MemberDeserializer implements JsonDeserializer<Member> {
#Override
public Member deserialize(JsonElement json, Type member, JsonDeserializationContext context) {
int myType = json.getAsJsonObject().get("type").getAsInt();
switch (myType) {
case 1: return context.deserialize(json, Silver.class);
case 2: return context.deserialize(json, Gold.class);
default: return null;
}
}
}
And... the output
Cloned!
{
"title": "MyClub",
"members": [
{
"silverData": "SomeSilverData",
"clsname": "gson.test.Silver",
"type": 1,
"name": "Jack"
},
{
"goldData": "SomeGoldData",
"extraData": "Extra Gold Data",
"clsname": "gson.test.Gold",
"type": 2,
"name": "Jill"
},
{
"silverData": "SomeSilverData",
"clsname": "gson.test.Silver",
"type": 1,
"name": "Mike"
}
]
}
I should note that my real use-case is one where performance should not be an issue, I'm loading a cache of objects from jSon text files so the frequency with this code is executed makes performance much less important than maintainability.
It looks like serializing/deserializing class hierarchies is a common problem.
There is even an "official" solution, inside extras directory of the official source repo (unfortunately it is not part of the Maven package though).
Please check:
The explanation: https://blog.novatec-gmbh.de/gson-object-hierarchies/
The solution: https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java. It is suggested to just copy/paste the source.
I am a beginner programmer and this is my first question on this forum.
I am writing a simple text adventure game using BlueJ as a compiler, and I am on a Mac. The problem I ran into is that I would like to make my code more self automated, but I cannot call a class with a string. The reason I want call the class and not have it all in an if function is so that I may incorporate more methods.
Here is how it will run currently:
public class textadventure {
public method(String room){
if(room==street){street.enterRoom();}
}
}
public class street{
public enterRoom(){
//do stuff and call other methods
}
}
The if statement tests for every class/room I create. What I would like the code to do is automatically make the string room into a class name that can be called. So it may act like so:
Public method(string room){
Class Room = room;
Room.enterRoom();
}
I have already looked into using Class.forName, but all the examples were too general for me to understand how to use the function. Any help would be greatly appreciated, and if there is any other necessary information (such as more example code) I am happy to provide it.
-Sebastien
Here is the full code:
import java.awt.*;
import javax.swing.*;
public class Player extends JApplet{
public String textOnScreen;
public void start(){
room("street1");
}
public void room(String room){
if(room=="street1"){
textOnScreen=street1.enterRoom();
repaint();
}
if(room=="street2"){
textOnScreen=street2.enterRoom();
repaint();
}
}
public void paint(Graphics g){
g.drawString(textOnScreen,5,15);
}
}
public abstract class street1
{
private static String textToScreen;
public static String enterRoom(){
textToScreen = "You are on a street running from North to South.";
return textToScreen;
}
}
public abstract class street2
{
private static String textToScreen;
public static String enterRoom(){
textToScreen = "You are on another street.";
return textToScreen;
}
}
Seeing as you are rather new to programming, I would recommend starting with some programs that are simpler than a full-fledged adventure game. You still haven't fully grasped some of the fundamentals of the Java syntax. Take, for example, the HelloWorld program:
public class HelloWorld {
public static void main(String[] args) {
String output = "Hello World!"
System.out.println(output);
}
}
Notice that public is lowercased. Public with a capital P is not the same as public.
Also notice that the String class has a capital S.* Again, capitalization matters, so string is not the same as String.
In addition, note that I didn't have to use String string = new String("string"). You can use String string = "string". This syntax runs faster and is easier to read.
When testing for string equality, you need to use String.equals instead of ==. This is because a == b checks for object equality (i.e. a and b occupy the same spot in memory) and stringOne.equals(stringTwo) checks to see if stringOne has the same characters in the same order as stringTwo regardless of where they are in memory.
Now, as for your question, I would recommend using either an Enum or a Map to keep track of which object to use.
For example:
public class Tester {
public enum Location {
ROOM_A("Room A", "You are going into Room A"),
ROOM_B("Room B", "You are going into Room B"),
OUTSIDE("Outside", "You are going outside");
private final String name;
private final String actionText;
private Location(String name, String actionText) {
this.name = name;
this.actionText = actionText;
}
public String getActionText() {
return this.actionText;
}
public String getName() {
return this.name;
}
public static Location findByName(String name) {
name = name.toUpperCase().replaceAll("\\s+", "_");
try {
return Enum.valueOf(Location.class, name);
} catch (IllegalArgumentException e) {
return null;
}
}
}
private Location currentLocation;
public void changeLocation(String locationName) {
Location location = Location.findByName(locationName);
if (location == null) {
System.out.println("Unknown room: " + locationName);
} else if (currentLocation != null && currentLocation.equals(location)) {
System.out.println("Already in room " + location.getName());
} else {
System.out.println(location.getActionText());
currentLocation = location;
}
}
public static void main(String[] args) {
Tester tester = new Tester();
tester.changeLocation("room a");
tester.changeLocation("room b");
tester.changeLocation("room c");
tester.changeLocation("room b");
tester.changeLocation("outside");
}
}
*This is the standard way of formating Java code. Class names are PascalCased while variable names are camelCased.
String className=getClassName();//Get class name from user here
String fnName=getMethodName();//Get function name from user here
Class params[] = {};
Object paramsObj[] = {};
Class thisClass = Class.forName(className);// get the Class
Object inst = thisClass.newInstance();// get an instance
// get the method
Method fn = thisClass.getDeclaredMethod(fnName, params);
// call the method
fn.invoke(inst, paramsObj);
The comments below your question are true - your code is very rough.
Anyway, if you have a method like
public void doSomething(String str) {
if (str.equals("whatever")) {
// do something
}
}
Then call it like
doSomething("whatever");
In Java, many classes have attributes, and you can and will often have multiple instances from the same class.
How would you identify which is which by name?
For example
class Room {
List<Monster> monsters = new ArrayList <Monster> ();
public Room (int monstercount) {
for (int i = 0; i < monstercount; ++i)
monsters.add (new Monster ());
}
// ...
}
Monsters can have attributes, and if one of them is dead, you can identify it more easily if you don't handle everything in Strings.