2222import javafx .application .Platform ;
2323import javafx .beans .InvalidationListener ;
2424import javafx .beans .Observable ;
25- import javafx .beans .property .BooleanProperty ;
2625import javafx .beans .property .ObjectProperty ;
27- import javafx .beans .property .SimpleBooleanProperty ;
2826import javafx .beans .property .SimpleObjectProperty ;
2927import javafx .beans .property .SimpleStringProperty ;
3028import javafx .beans .property .StringProperty ;
3836import org .jackhuang .hmcl .game .Version ;
3937import org .jackhuang .hmcl .ui .WeakListenerHolder ;
4038import org .jackhuang .hmcl .util .GUID ;
39+ import org .jackhuang .hmcl .util .PortablePath ;
4140import org .jackhuang .hmcl .util .ToStringBuilder ;
4241import org .jackhuang .hmcl .util .javafx .ObservableHelper ;
42+ import org .jetbrains .annotations .NotNullByDefault ;
4343import org .jetbrains .annotations .Nullable ;
4444
4545import java .lang .reflect .Type ;
5555 * @author huangyuhui
5656 */
5757@ JsonAdapter (Profile .Serializer .class )
58+ @ NotNullByDefault
5859public final class Profile implements Observable {
5960 private final WeakListenerHolder listenerHolder = new WeakListenerHolder ();
6061 private final HMCLGameRepository repository ;
@@ -83,26 +84,30 @@ public StringProperty selectedVersionProperty() {
8384 return selectedVersion ;
8485 }
8586
86- public String getSelectedVersion () {
87+ public @ Nullable String getSelectedVersion () {
8788 return selectedVersion .get ();
8889 }
8990
90- public void setSelectedVersion (String selectedVersion ) {
91+ public void setSelectedVersion (@ Nullable String selectedVersion ) {
9192 this .selectedVersion .set (selectedVersion );
9293 }
9394
94- private final ObjectProperty <Path > gameDir ;
95+ /// The game directory path.
96+ private final ObjectProperty <PortablePath > path ;
9597
96- public ObjectProperty <Path > gameDirProperty () {
97- return gameDir ;
98+ /// Returns the game directory path property.
99+ public ObjectProperty <PortablePath > pathProperty () {
100+ return path ;
98101 }
99102
100- public Path getGameDir () {
101- return gameDir .get ();
103+ /// Returns the game directory path.
104+ public PortablePath getPath () {
105+ return path .get ();
102106 }
103107
104- public void setGameDir (Path gameDir ) {
105- this .gameDir .set (gameDir );
108+ /// Sets the game directory path.
109+ public void setPath (PortablePath path ) {
110+ this .path .set (Objects .requireNonNull (path ));
106111 }
107112
108113 private final SimpleStringProperty name ;
@@ -119,38 +124,24 @@ public void setName(String name) {
119124 this .name .set (name );
120125 }
121126
122- private final BooleanProperty useRelativePath = new SimpleBooleanProperty (this , "useRelativePath" , false );
123-
124- public BooleanProperty useRelativePathProperty () {
125- return useRelativePath ;
126- }
127-
128- public boolean isUseRelativePath () {
129- return useRelativePath .get ();
130- }
131-
132- public void setUseRelativePath (boolean useRelativePath ) {
133- this .useRelativePath .set (useRelativePath );
134- }
135-
136127 public Profile (String name , Path initialGameDir ) {
137- this (name , initialGameDir , null , false );
128+ this (name , PortablePath . fromPath ( initialGameDir ) );
138129 }
139130
140- public Profile (String name , Path initialGameDir , @ Nullable String selectedVersion , boolean useRelativePath ) {
141- this (GUID .random (), name , initialGameDir , selectedVersion , useRelativePath );
131+ /// Creates a profile.
132+ public Profile (String name , PortablePath path ) {
133+ this (GUID .random (), name , path , null );
142134 }
143135
144136 /// Creates a profile with an explicit stable ID.
145- Profile (GUID id , String name , Path initialGameDir , @ Nullable String selectedVersion , boolean useRelativePath ) {
137+ Profile (GUID id , String name , PortablePath path , @ Nullable String selectedVersion ) {
146138 this .id .set (Objects .requireNonNull (id ));
147139 this .name = new SimpleStringProperty (this , "name" , name );
148- gameDir = new SimpleObjectProperty <>(this , "gameDir " , initialGameDir );
149- repository = new HMCLGameRepository (this , initialGameDir );
140+ this . path = new SimpleObjectProperty <>(this , "path " , Objects . requireNonNull ( path ) );
141+ repository = new HMCLGameRepository (this , path . toPath () );
150142 this .selectedVersion .set (selectedVersion );
151- this .useRelativePath .set (useRelativePath );
152143
153- gameDir . addListener ((a , b , newValue ) -> repository .changeDirectory (newValue ));
144+ this . path . addListener ((a , b , newValue ) -> repository .changeDirectory (newValue . toPath () ));
154145 this .selectedVersion .addListener (o -> checkSelectedVersion ());
155146 listenerHolder .add (EventBus .EVENT_BUS .channel (RefreshedVersionsEvent .class ).registerWeak (event -> checkSelectedVersion (), EventPriority .HIGHEST ));
156147
@@ -186,17 +177,15 @@ public DefaultDependencyManager getDependency(DownloadProvider downloadProvider)
186177 @ Override
187178 public String toString () {
188179 return new ToStringBuilder (this )
189- .append ("gameDir " , getGameDir ())
180+ .append ("path " , getPath ())
190181 .append ("name" , getName ())
191- .append ("useRelativePath" , isUseRelativePath ())
192182 .toString ();
193183 }
194184
195185 private void addPropertyChangedListener (InvalidationListener listener ) {
196186 id .addListener (listener );
197187 name .addListener (listener );
198- gameDir .addListener (listener );
199- useRelativePath .addListener (listener );
188+ path .addListener (listener );
200189 selectedVersion .addListener (listener );
201190 }
202191
@@ -216,39 +205,41 @@ private void invalidate() {
216205 Platform .runLater (observableHelper ::invalidate );
217206 }
218207
219- public record ProfileVersion (Profile profile , String version ) {
208+ public record ProfileVersion (Profile profile , @ Nullable String version ) {
220209 }
221210
222211 public static final class Serializer implements JsonSerializer <Profile >, JsonDeserializer <Profile > {
223212 @ Override
224- public JsonElement serialize (Profile src , Type typeOfSrc , JsonSerializationContext context ) {
213+ public JsonElement serialize (@ Nullable Profile src , Type typeOfSrc , JsonSerializationContext context ) {
225214 if (src == null )
226215 return JsonNull .INSTANCE ;
227216
228217 JsonObject jsonObject = new JsonObject ();
229218 jsonObject .add ("id" , context .serialize (src .getId (), GUID .class ));
230219 jsonObject .addProperty ("name" , src .getName ());
231- jsonObject .addProperty ("gameDir" , src .getGameDir ().toString ());
232- jsonObject .addProperty ("useRelativePath" , src .isUseRelativePath ());
220+ jsonObject .add ("path" , context .serialize (src .getPath (), PortablePath .class ));
233221 jsonObject .addProperty ("selectedMinecraftVersion" , src .getSelectedVersion ());
234222
235223 return jsonObject ;
236224 }
237225
238226 @ Override
239- public Profile deserialize (JsonElement json , Type typeOfT , JsonDeserializationContext context ) throws JsonParseException {
227+ public @ Nullable Profile deserialize (@ Nullable JsonElement json , Type typeOfT , JsonDeserializationContext context ) throws JsonParseException {
240228 if (!(json instanceof JsonObject obj )) return null ;
241229 GUID id = context .deserialize (obj .get ("id" ), GUID .class );
242230 if (id == null ) {
243231 throw new JsonParseException ("Profile ID cannot be null" );
244232 }
245- String gameDir = Optional .ofNullable (obj .get ("gameDir" )).map (JsonElement ::getAsString ).orElse ("" );
233+ PortablePath path = context .deserialize (obj .get ("path" ), PortablePath .class );
234+ if (path == null ) {
235+ String gameDir = Optional .ofNullable (obj .get ("gameDir" )).map (JsonElement ::getAsString ).orElse ("" );
236+ path = PortablePath .of (gameDir );
237+ }
246238
247239 return new Profile (id ,
248240 Optional .ofNullable (obj .get ("name" )).map (JsonElement ::getAsString ).orElse ("Default" ),
249- Path .of (gameDir ),
250- Optional .ofNullable (obj .get ("selectedMinecraftVersion" )).map (JsonElement ::getAsString ).orElse ("" ),
251- Optional .ofNullable (obj .get ("useRelativePath" )).map (JsonElement ::getAsBoolean ).orElse (false ));
241+ path ,
242+ Optional .ofNullable (obj .get ("selectedMinecraftVersion" )).map (JsonElement ::getAsString ).orElse ("" ));
252243 }
253244
254245 }
0 commit comments