• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Camelcade / Perl5-IDEA / #525521600

27 Jun 2025 03:55PM UTC coverage: 82.369% (+0.009%) from 82.36%
#525521600

push

github

hurricup
Build 252.23591.19

30871 of 37479 relevant lines covered (82.37%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.51
/plugin/core/src/main/java/com/perl5/lang/perl/idea/run/GenericPerlRunConfiguration.java
1
/*
2
 * Copyright 2015-2025 Alexandr Evstigneev
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.perl5.lang.perl.idea.run;
18

19
import com.intellij.execution.CommonProgramRunConfigurationParameters;
20
import com.intellij.execution.ExecutionException;
21
import com.intellij.execution.Executor;
22
import com.intellij.execution.configurations.*;
23
import com.intellij.execution.process.ProcessHandler;
24
import com.intellij.execution.runners.ExecutionEnvironment;
25
import com.intellij.execution.runners.RunConfigurationWithSuppressedDefaultRunAction;
26
import com.intellij.execution.ui.ConsoleView;
27
import com.intellij.openapi.application.ApplicationManager;
28
import com.intellij.openapi.application.ReadAction;
29
import com.intellij.openapi.components.PathMacroManager;
30
import com.intellij.openapi.diagnostic.Logger;
31
import com.intellij.openapi.project.Project;
32
import com.intellij.openapi.projectRoots.Sdk;
33
import com.intellij.openapi.projectRoots.impl.PerlSdkTable;
34
import com.intellij.openapi.util.InvalidDataException;
35
import com.intellij.openapi.util.NlsSafe;
36
import com.intellij.openapi.util.WriteExternalException;
37
import com.intellij.openapi.util.text.StringUtil;
38
import com.intellij.openapi.vfs.VfsUtil;
39
import com.intellij.openapi.vfs.VirtualFile;
40
import com.intellij.util.Function;
41
import com.intellij.util.concurrency.annotations.RequiresReadLock;
42
import com.intellij.util.containers.ContainerUtil;
43
import com.intellij.util.execution.ParametersListUtil;
44
import com.intellij.util.net.NetUtils;
45
import com.intellij.util.xmlb.XmlSerializer;
46
import com.perl5.PerlBundle;
47
import com.perl5.lang.perl.idea.execution.PerlCommandLine;
48
import com.perl5.lang.perl.idea.execution.PerlTerminalExecutionConsole;
49
import com.perl5.lang.perl.idea.project.PerlProjectManager;
50
import com.perl5.lang.perl.idea.run.debugger.PerlDebugOptions;
51
import com.perl5.lang.perl.idea.sdk.host.PerlConsoleView;
52
import com.perl5.lang.perl.idea.sdk.host.PerlHostData;
53
import com.perl5.lang.perl.util.PerlFileUtil;
54
import com.perl5.lang.perl.util.PerlRunUtil;
55
import org.jdom.Element;
56
import org.jetbrains.annotations.Contract;
57
import org.jetbrains.annotations.NotNull;
58
import org.jetbrains.annotations.Nullable;
59

60
import java.io.File;
61
import java.nio.charset.Charset;
62
import java.nio.charset.UnsupportedCharsetException;
63
import java.util.*;
64
import java.util.stream.Collectors;
65

66
import static com.intellij.execution.configurations.GeneralCommandLine.ParentEnvironmentType.CONSOLE;
67
import static com.intellij.execution.configurations.GeneralCommandLine.ParentEnvironmentType.NONE;
68

69
public abstract class GenericPerlRunConfiguration extends LocatableConfigurationBase<LocatableRunConfigurationOptions>
70
  implements CommonProgramRunConfigurationParameters,
71
             RunConfigurationWithSuppressedDefaultRunAction,
72
             PerlDebugOptions {
73
  public static final Function<String, List<String>> FILES_PARSER = text -> StringUtil.split(text.trim(), "||");
1✔
74
  public static final Function<List<String>, String> FILES_JOINER = strings ->
1✔
75
    StringUtil.join(ContainerUtil.filter(strings, StringUtil::isNotEmpty), "||");
1✔
76
  public static final Function<String, List<String>> PREREQUISITES_PARSER = text -> Arrays.stream(text.trim().split("\\s*,\\s*"))
1✔
77
    .filter( StringUtil::isNotEmpty).sorted().toList() ;
1✔
78
  @SuppressWarnings("StaticMethodOnlyUsedInOneClass")
79
  public static final Function<List<String>, String> PREREQUISITES_JOINER = strings -> strings.stream()
1✔
80
    .filter(StringUtil::isNotEmpty).map(String::trim).sorted().collect(Collectors.joining(","));
1✔
81

82
  private static final Logger LOG = Logger.getInstance(GenericPerlRunConfiguration.class);
1✔
83

84
  private String myScriptPath;
85
  private String myScriptArguments;
86

87
  private String myPerlArguments = "";
1✔
88
  private String myWorkingDirectory;
89
  private Map<String, String> myEnvironments = new HashMap<>();
1✔
90
  private boolean myPassParentEnvironments = true;
1✔
91
  private String myConsoleCharset;
92
  private boolean myUseAlternativeSdk;
93
  private String myAlternativeSdkName;
94
  private @NotNull String myRequiredModules = "";
1✔
95

96
  // debugging-related options
97
  private String myScriptCharset = "utf8";
1✔
98
  private String myStartMode = "RUN";
1✔
99
  private boolean myIsNonInteractiveModeEnabled = false;
1✔
100
  private boolean myIsCompileTimeBreakpointsEnabled = false;
1✔
101
  private String myInitCode = "";
1✔
102

103
  private transient Integer myDebugPort;
104

105
  public GenericPerlRunConfiguration(@NotNull Project project, @NotNull ConfigurationFactory factory, @Nullable String name) {
106
    super(project, factory, name);
1✔
107
  }
1✔
108

109
  @Override
110
  public void readExternal(@NotNull Element element) throws InvalidDataException {
111
    // this is actually redundant thing. For some reason expanding done by the platform, but collapsing need to be done manually. But this
112
    // feels really unreliable and bad for testing. So, we'll do it manually.
113
    PathMacroManager.getInstance(getProject()).expandPaths(element);
1✔
114
    super.readExternal(element);
1✔
115
    XmlSerializer.deserializeInto(this, element);
1✔
116
  }
1✔
117

118
  @Override
119
  public void writeExternal(@NotNull Element element) throws WriteExternalException {
120
    super.writeExternal(element);
1✔
121
    XmlSerializer.serializeInto(this, element);
1✔
122
    PathMacroManager.getInstance(getProject()).collapsePathsRecursively(element);
1✔
123
  }
1✔
124

125
  @Override
126
  public final @Nullable PerlRunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment executionEnvironment)
127
    throws ExecutionException {
128
    var runner = executionEnvironment.getRunner();
1✔
129
    if (!(runner instanceof GenericPerlProgramRunner genericPerlProgramRunner)) {
1✔
130
      LOG.error("GenericPerlProgramRunner expected, got " + runner);
×
131
      throw new ExecutionException(
×
132
        PerlBundle.message("dialog.message.wrong.runner.used.to.run.perl.configuration.please.report.to.perl.plugin.developers"));
×
133
    }
134
    return genericPerlProgramRunner.createState(executionEnvironment);
1✔
135
  }
136

137
  /**
138
   * @return local path to the perl interpreter for this run configuration
139
   * @throws ExecutionException with human readable error message if interpreter path is empty
140
   */
141
  protected @NotNull String getEffectiveInterpreterPath() throws ExecutionException {
142
    Sdk effectiveSdk = getEffectiveSdk();
1✔
143
    String interpreterPath = PerlProjectManager.getInterpreterPath(effectiveSdk);
1✔
144
    if (StringUtil.isEmpty(interpreterPath)) {
1✔
145
      throw new ExecutionException(PerlBundle.message("perl.run.error.no.interpreter.path", effectiveSdk));
×
146
    }
147
    return interpreterPath;
1✔
148
  }
149

150
  public @NotNull Sdk getEffectiveSdk() throws ExecutionException {
151
    Sdk perlSdk;
152
    if (isUseAlternativeSdk()) {
1✔
153
      String alternativeSdkName = getAlternativeSdkName();
×
154
      if (StringUtil.isEmpty(alternativeSdkName)) {
×
155
        throw new ExecutionException(PerlBundle.message("perl.run.error.no.alternative.sdk.selected"));
×
156
      }
157
      perlSdk = PerlSdkTable.getInstance().findJdk(alternativeSdkName);
×
158
      if (perlSdk == null) {
×
159
        throw new ExecutionException(PerlBundle.message("perl.run.error.no.alternative.sdk", alternativeSdkName));
×
160
      }
161
      return perlSdk;
×
162
    }
163
    else {
164
      perlSdk = PerlProjectManager.getSdk(getProject());
1✔
165
      if (perlSdk == null) {
1✔
166
        throw new ExecutionException(PerlBundle.message("perl.run.error.no.sdk", getProject().getName()));
×
167
      }
168
    }
169
    return perlSdk;
1✔
170
  }
171

172
  @Override
173
  public String suggestedName() {
174
    List<VirtualFile> targetFiles = computeTargetFiles();
1✔
175
    return targetFiles.isEmpty() ? null : targetFiles.getFirst().getName();
1✔
176
  }
177

178
  protected @NotNull List<VirtualFile> computeTargetFiles() {
179
    return computeVirtualFilesFromPaths(getScriptPath());
1✔
180
  }
181

182
  private @NotNull VirtualFile computeNonNullScriptFile() throws ExecutionException {
183
    List<VirtualFile> targetFiles = computeTargetFiles();
1✔
184
    if (targetFiles.isEmpty()) {
1✔
185
      throw new ExecutionException(PerlBundle.message("perl.run.error.script.missing", getScriptPath()));
×
186
    }
187
    return targetFiles.getFirst();
1✔
188
  }
189

190
  public @NotNull String getRequiredModules() {
191
    return myRequiredModules;
1✔
192
  }
193

194
  public @NotNull List<String> getRequiredModulesList() {
195
    return PREREQUISITES_PARSER.fun(myRequiredModules);
1✔
196
  }
197

198
  public void setRequiredModules(@NotNull String requiredModules) {
199
    myRequiredModules = PREREQUISITES_JOINER.fun(PREREQUISITES_PARSER.fun(requiredModules));
1✔
200
  }
1✔
201

202
  public @NlsSafe String getConsoleCharset() {
203
    return myConsoleCharset;
1✔
204
  }
205

206
  public void setConsoleCharset(String charset) {
207
    myConsoleCharset = charset;
1✔
208
  }
1✔
209

210
  public String getScriptPath() {
211
    return myScriptPath;
1✔
212
  }
213

214
  public void setScriptPath(String scriptPath) {
215
    myScriptPath = scriptPath;
1✔
216
  }
1✔
217

218
  public String getAlternativeSdkName() {
219
    return myAlternativeSdkName;
1✔
220
  }
221

222
  public void setAlternativeSdkName(String name) {
223
    myAlternativeSdkName = name;
1✔
224
  }
1✔
225

226
  public boolean isUseAlternativeSdk() {
227
    return myUseAlternativeSdk;
1✔
228
  }
229

230
  public void setUseAlternativeSdk(boolean value) {
231
    myUseAlternativeSdk = value;
1✔
232
  }
1✔
233

234
  @Override
235
  public @Nullable String getProgramParameters() {
236
    return myScriptArguments;
1✔
237
  }
238

239
  @Override
240
  public void setProgramParameters(@Nullable String s) {
241
    myScriptArguments = s;
1✔
242
  }
1✔
243

244
  @Override
245
  public @Nullable String getWorkingDirectory() {
246
    return myWorkingDirectory;
1✔
247
  }
248

249
  /**
250
   * @return virtual file for a working directory explicitly set by user
251
   */
252
  protected @Nullable VirtualFile computeExplicitWorkingDirectory() {
253
    String workingDirectory = getWorkingDirectory();
1✔
254
    if (StringUtil.isEmpty(workingDirectory)) {
1✔
255
      return null;
1✔
256
    }
257
    return VfsUtil.findFileByIoFile(new File(workingDirectory), true);
×
258
  }
259

260
  @Override
261
  public void setWorkingDirectory(@Nullable String s) {
262
    myWorkingDirectory = s;
1✔
263
  }
1✔
264

265
  @Override
266
  public @NotNull Map<String, String> getEnvs() {
267
    return myEnvironments;
1✔
268
  }
269

270
  @Override
271
  public void setEnvs(@NotNull Map<String, String> map) {
272
    myEnvironments = map;
1✔
273
  }
1✔
274

275
  @Override
276
  public boolean isPassParentEnvs() {
277
    return myPassParentEnvironments;
1✔
278
  }
279

280
  @Override
281
  public void setPassParentEnvs(boolean b) {
282
    myPassParentEnvironments = b;
1✔
283
  }
1✔
284

285
  public String getPerlArguments() {
286
    return myPerlArguments;
1✔
287
  }
288

289
  protected @NotNull List<String> getPerlArgumentsList() {
290
    String perlArguments = getPerlArguments();
1✔
291
    return StringUtil.isEmpty(perlArguments) ? Collections.emptyList() : ParametersListUtil.parse(perlArguments);
1✔
292
  }
293

294
  public void setPerlArguments(String perlArguments) {
295
    this.myPerlArguments = perlArguments;
1✔
296
  }
1✔
297

298
  @Override
299
  public String getStartMode() {
300
    return myStartMode;
1✔
301
  }
302

303
  @Override
304
  public void setStartMode(String startMode) {
305
    this.myStartMode = startMode;
1✔
306
  }
1✔
307

308
  @Override
309
  public String getPerlRole() {
310
    return PerlDebugOptions.ROLE_SERVER;
1✔
311
  }
312

313
  @Override
314
  public String getHostToBind() {
315
    return LOCAL_DEBUG_HOST;
1✔
316
  }
317

318
  @Override
319
  public int getDebugPort() throws ExecutionException {
320
    if (myDebugPort == null) {
1✔
321
      myDebugPort = NetUtils.tryToFindAvailableSocketPort();
1✔
322
      if (myDebugPort == -1) {
1✔
323
        throw new ExecutionException(PerlBundle.message("dialog.message.no.free.port.to.work.on"));
×
324
      }
325
    }
326

327
    return myDebugPort;
1✔
328
  }
329

330
  @Override
331
  public String getRemoteProjectRoot() {
332
    return getProject().getBasePath();
×
333
  }
334

335
  @Override
336
  public String getScriptCharset() {
337
    return myScriptCharset;
1✔
338
  }
339

340
  @Override
341
  public void setScriptCharset(String scriptCharset) {
342
    this.myScriptCharset = scriptCharset;
1✔
343
  }
1✔
344

345
  @Override
346
  public boolean isNonInteractiveModeEnabled() {
347
    return myIsNonInteractiveModeEnabled;
1✔
348
  }
349

350
  @Override
351
  public void setNonInteractiveModeEnabled(boolean nonInteractiveModeEnabled) {
352
    myIsNonInteractiveModeEnabled = nonInteractiveModeEnabled;
1✔
353
  }
1✔
354

355
  @Override
356
  public boolean isCompileTimeBreakpointsEnabled() {
357
    return myIsCompileTimeBreakpointsEnabled;
1✔
358
  }
359

360
  @Override
361
  public void setCompileTimeBreakpointsEnabled(boolean compileTimeBreakpointsEnabled) {
362
    myIsCompileTimeBreakpointsEnabled = compileTimeBreakpointsEnabled;
1✔
363
  }
1✔
364

365
  public @NotNull PerlCommandLine createCommandLine(@NotNull PerlRunProfileState perlRunProfileState) throws ExecutionException {
366
    PerlCommandLine commandLine = createBaseCommandLine(perlRunProfileState);
1✔
367
    commandLine.withParentEnvironmentType(isPassParentEnvs() ? CONSOLE : NONE);
1✔
368
    commandLine.withWorkDirectory(computeWorkingDirectory(perlRunProfileState.getEnvironment().getProject()));
1✔
369
    commandLine.withCharset(computeCharset());
1✔
370

371
    return commandLine.withPty(isUsePty());
1✔
372
  }
373

374
  protected boolean isUsePty() {
375
    return true;
1✔
376
  }
377

378
  protected @NotNull Charset computeCharset() throws ExecutionException {
379
    String charsetName = getConsoleCharset();
1✔
380
    if (!StringUtil.isEmpty(charsetName)) {
1✔
381
      try {
382
        return Charset.forName(charsetName);
1✔
383
      }
384
      catch (UnsupportedCharsetException e) {
×
385
        throw new ExecutionException(PerlBundle.message("perl.run.error.unknown.charset", charsetName));
×
386
      }
387
    }
388
    else {
389
      return computeNonNullScriptFile().getCharset();
×
390
    }
391
  }
392

393
  /**
394
   * @see PerlRemoteDebuggingRunProfileState#computeLocalProjectPath(PerlRemoteDebuggingConfiguration)
395
   */
396
  protected @Nullable String computeWorkingDirectory(@NotNull Project project) throws ExecutionException {
397
    String workDirectory = getWorkingDirectory();
1✔
398
    if (StringUtil.isNotEmpty(workDirectory)) {
1✔
399
      return workDirectory;
×
400
    }
401
    return ReadAction.compute(() -> computeWorkingDirectory(project, computeNonNullScriptFile()));
1✔
402
  }
403

404
  protected @NotNull PerlCommandLine createBaseCommandLine(@NotNull PerlRunProfileState perlRunProfileState) throws ExecutionException {
405
    ExecutionEnvironment executionEnvironment = perlRunProfileState.getEnvironment();
1✔
406
    Project project = executionEnvironment.getProject();
1✔
407
    List<String> additionalPerlArguments = perlRunProfileState.getAdditionalPerlArguments(this);
1✔
408
    Map<String, String> additionalEnvironmentVariables = perlRunProfileState.getAdditionalEnvironmentVariables();
1✔
409

410
    PerlCommandLine commandLine = PerlRunUtil.getPerlCommandLine(
1✔
411
      project, getEffectiveSdk(), computeNonNullScriptFile(), ContainerUtil.concat(getPerlArgumentsList(), additionalPerlArguments),
1✔
412
      getScriptArguments());
1✔
413

414
    if (commandLine == null) {
1✔
415
      throw new ExecutionException(PerlBundle.message("perl.run.error.sdk.corrupted", getEffectiveSdk()));
×
416
    }
417

418
    Map<String, String> environment = new HashMap<>(getEnvs());
1✔
419
    environment.putAll(additionalEnvironmentVariables);
1✔
420
    commandLine.withEnvironment(environment);
1✔
421
    return commandLine;
1✔
422
  }
423

424
  @RequiresReadLock
425
  protected static @Nullable String computeWorkingDirectory(@NotNull Project project, @NotNull VirtualFile virtualFile) {
426
    var fileContentRoot = PerlFileUtil.getContentRoot(project, virtualFile);
1✔
427
    if (fileContentRoot != null) {
1✔
428
      return fileContentRoot.getPath();
1✔
429
    }
430
    return project.getBasePath();
×
431
  }
432

433
  protected @NotNull List<String> getScriptArguments() {
434
    String programArguments = getProgramParameters();
1✔
435
    return StringUtil.isEmpty(programArguments) ? Collections.emptyList() : ParametersListUtil.parse(programArguments);
1✔
436
  }
437

438
  @Override
439
  public String getInitCode() {
440
    return myInitCode;
1✔
441
  }
442

443
  @Override
444
  public void setInitCode(String initCode) {
445
    this.myInitCode = initCode;
1✔
446
  }
1✔
447

448
  /**
449
   * @return list of VirtualFiles pointed by  {@code paths} joined with pipe. Reverse of {@link #computePathsFromVirtualFiles(List)}
450
   */
451
  public static @NotNull List<VirtualFile> computeVirtualFilesFromPaths(@Nullable String paths) {
452
    if (StringUtil.isEmpty(paths)) {
1✔
453
      return Collections.emptyList();
×
454
    }
455
    List<String> pathNames = FILES_PARSER.fun(paths);
1✔
456
    if (pathNames.isEmpty()) {
1✔
457
      return Collections.emptyList();
×
458
    }
459
    List<VirtualFile> virtualFiles = new ArrayList<>(pathNames.size());
1✔
460
    for (String pathName : pathNames) {
1✔
461
      if (StringUtil.isEmpty(pathName)) {
1✔
462
        continue;
×
463
      }
464
      ContainerUtil.addIfNotNull(virtualFiles, VfsUtil.findFileByIoFile(new File(pathName), false));
1✔
465
    }
1✔
466
    return virtualFiles;
1✔
467
  }
468

469
  /**
470
   * @return paths of {@code virtualFiles} joined with pipe, reverse of {@link #computeVirtualFilesFromPaths(String)}
471
   */
472
  public static @NotNull String computePathsFromVirtualFiles(@NotNull List<? extends VirtualFile> virtualFiles) {
473
    return FILES_JOINER.fun(ContainerUtil.map(virtualFiles, VirtualFile::getPath));
1✔
474
  }
475

476
  public @NotNull ConsoleView createConsole(@NotNull PerlRunProfileState runProfileState) throws ExecutionException {
477
    ExecutionEnvironment executionEnvironment = runProfileState.getEnvironment();
1✔
478
    PerlConsoleView console = ApplicationManager.getApplication().isUnitTestMode() ?
1✔
479
                              new PerlRunConsole(runProfileState.getEnvironment().getProject()) :
1✔
480
                              new PerlTerminalExecutionConsole(executionEnvironment.getProject());
1✔
481
    return console.withHostData(PerlHostData.from(getEffectiveSdk()));
1✔
482
  }
483

484
  /**
485
   * May patch {@code processHandler} created with {@code runProfileState} from current run configuration.
486
   *
487
   * @return patched process handler
488
   */
489
  protected @NotNull ProcessHandler doPatchProcessHandler(@NotNull ProcessHandler processHandler) {
490
    return processHandler;
1✔
491
  }
492

493
  /**
494
   * Method checks if specified script(s) path is ok.
495
   *
496
   * @throws RuntimeConfigurationException with human-readable error message
497
   */
498
  protected void checkConfigurationScriptPath() throws RuntimeConfigurationException {
499
    if (StringUtil.isEmptyOrSpaces(getScriptPath())) {
1✔
500
      throw new RuntimeConfigurationException(PerlBundle.message("perl.run.error.no.script.set"));
×
501
    }
502
    if (computeTargetFiles().isEmpty()) {
1✔
503
      throw new RuntimeConfigurationException(PerlBundle.message("perl.run.error.no.script.found", getScriptPath()));
×
504
    }
505
  }
1✔
506

507
  @Override
508
  public void checkConfiguration() throws RuntimeConfigurationException {
509
    checkConfigurationScriptPath();
1✔
510
    try {
511
      getEffectiveInterpreterPath();
1✔
512
    }
513
    catch (ExecutionException e) {
×
514
      throw new RuntimeConfigurationException(e.getMessage());
×
515
    }
1✔
516
  }
1✔
517

518
  /**
519
   * If profileState was build with {@link GenericPerlRunConfiguration}, patches {@code processHandler} using
520
   * {@link #doPatchProcessHandler(ProcessHandler)}
521
   *
522
   * @return patched process handler
523
   */
524
  @Contract("null,_->null; !null,_->!null")
525
  static @Nullable ProcessHandler patchProcessHandler(@Nullable ProcessHandler processHandler,
526
                                                      @NotNull PerlRunProfileState runProfileState) {
527
    if (processHandler == null) {
1✔
528
      return null;
×
529
    }
530
    RunProfile runConfiguration = runProfileState.getEnvironment().getRunProfile();
1✔
531
    return runConfiguration instanceof GenericPerlRunConfiguration perlRunConfiguration ?
1✔
532
      perlRunConfiguration.doPatchProcessHandler(processHandler) :
1✔
533
           processHandler;
×
534
  }
535
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc