##### Coverage Report - org.apache.commons.configuration.PropertiesConfigurationLayout

Classes in this File | Line Coverage | Branch Coverage | Complexity

PropertiesConfigurationLayout

99% | 143/145

100% | 35/35

2.4444444444444446;2,444

PropertiesConfigurationLayout$PropertyLayoutData

96% | 23/24

100% | 5/5

2.4444444444444446;2,444

1

     /*

2

      * Licensed to the Apache Software Foundation (ASF) under one or more

3

      * contributor license agreements.  See the NOTICE file distributed with

4

      * this work for additional information regarding copyright ownership.

5

      * The ASF licenses this file to You under the Apache License, Version 2.0

6

      * (the "License"); you may not use this file except in compliance with

7

      * the License.  You may obtain a copy of the License at

8

      *

9

      *     http://www.apache.org/licenses/LICENSE-2.0

10

      *

11

      * Unless required by applicable law or agreed to in writing, software

12

      * distributed under the License is distributed on an "AS IS" BASIS,

13

      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

14

      * See the License for the specific language governing permissions and

15

      * limitations under the License.

16

      */

17

     package org.apache.commons.configuration;

18

19

     import java.io.IOException;

20

     import java.io.Reader;

21

     import java.io.Writer;

22

     import java.util.Iterator;

23

     import java.util.List;

24

     import java.util.Map;

25

     import java.util.Set;

26

27

     import org.apache.commons.collections.map.LinkedMap;

28

     import org.apache.commons.configuration.event.ConfigurationEvent;

29

     import org.apache.commons.configuration.event.ConfigurationListener;

30

     import org.apache.commons.lang.StringUtils;

31

32

     /**

33

      * <p>

34

      * A helper class used by <code>{@link PropertiesConfiguration}</code> to keep

35

      * the layout of a properties file.

36

      * </p>

37

      * <p>

38

      * Instances of this class are associated with a

39

      * <code>PropertiesConfiguration</code> object. They are responsible for

40

      * analyzing properties files and for extracting as much information about the

41

      * file layout (e.g. empty lines, comments) as possible. When the properties

42

      * file is written back again it should be close to the original.

43

      * </p>

44

      * <p>

45

      * The <code>PropertiesConfigurationLayout</code> object associated with a

46

      * <code>PropertiesConfiguration</code> object can be obtained using the

47

      * <code>getLayout()</code> method of the configuration. Then the methods

48

      * provided by this class can be used to alter the properties file's layout.

49

      * </p>

50

      * <p>

51

      * Implementation note: This is a very simple implementation, which is far away

52

      * from being perfect, i.e. the original layout of a properties file won't be

53

      * reproduced in all cases. One limitation is that comments for multi-valued

54

      * property keys are concatenated. Maybe this implementation can later be

55

      * improved.

56

      * </p>

57

      * <p>

58

      * To get an impression how this class works consider the following properties

59

      * file:

60

      * </p>

61

      * <p>

62

      *

63

      * <pre>

64

      * # A demo configuration file

65

      * # for Demo App 1.42

66

      *

67

      * # Application name

68

      * AppName=Demo App

69

      *

70

      * # Application vendor

71

      * AppVendor=DemoSoft

72

      *

73

      *

74

      * # GUI properties

75

      * # Window Color

76

      * windowColors=0xFFFFFF,0x000000

77

      *

78

      * # Include some setting

79

      * include=settings.properties

80

      * # Another vendor

81

      * AppVendor=TestSoft

82

      * </pre>

83

      *

84

      * </p>

85

      * <p>

86

      * For this example the following points are relevant:

87

      * </p>

88

      * <p>

89

      * <ul>

90

      * <li>The first two lines are set as header comment. The header comment is

91

      * determined by the last blanc line before the first property definition.</li>

92

      * <li>For the property <code>AppName</code> one comment line and one

93

      * leading blanc line is stored.</li>

94

      * <li>For the property <code>windowColors</code> two comment lines and two

95

      * leading blanc lines are stored.</li>

96

      * <li>Include files is something this class cannot deal with well. When saving

97

      * the properties configuration back, the included properties are simply

98

      * contained in the original file. The comment before the include property is

99

      * skipped.</li>

100

      * <li>For all properties except for <code>AppVendor</code> the &quot;single

101

      * line&quot; flag is set. This is relevant only for <code>windowColors</code>,

102

      * which has multiple values defined in one line using the separator character.</li>

103

      * <li>The <code>AppVendor</code> property appears twice. The comment lines

104

      * are concatenated, so that <code>layout.getComment("AppVendor");</code> will

105

      * result in <code>Application vendor&lt;CR&gt;Another vendor</code>, whith

106

      * <code>&lt;CR&gt;</code> meaning the line separator. In addition the

107

      * &quot;single line&quot; flag is set to <b>false</b> for this property. When

108

      * the file is saved, two property definitions will be written (in series).</li>

109

      * </ul>

110

      * </p>

111

      *

112

      * @author <a

113

      * href="http://jakarta.apache.org/commons/configuration/team-list.html">Commons

114

      * Configuration team</a>

115

      * @version $Id: PropertiesConfigurationLayout.java 439648 2006-09-02 20:42:10Z oheger $

116

      * @since 1.3

117

      */

118

813

     public class PropertiesConfigurationLayout implements ConfigurationListener

119

     {

120

         /** Constant for the line break character. */

121

20

         private static final String CR = System.getProperty("line.separator");

122

123

         /** Constant for the default comment prefix. */

124

         private static final String COMMENT_PREFIX = "# ";

125

126

         /** Stores the associated configuration object. */

127

         private PropertiesConfiguration configuration;

128

129

         /** Stores a map with the contained layout information. */

130

         private Map layoutData;

131

132

         /** Stores the header comment. */

133

         private String headerComment;

134

135

         /** A counter for determining nested load calls. */

136

         private int loadCounter;

137

138

         /** Stores the force single line flag. */

139

         private boolean forceSingleLine;

140

141

         /**

142

          * Creates a new instance of <code>PropertiesConfigurationLayout</code>

143

          * and initializes it with the associated configuration object.

144

          *

145

          * @param config the configuration (must not be <b>null</b>)

146

          */

147

         public PropertiesConfigurationLayout(PropertiesConfiguration config)

148

         {

149

417

             this(config, null);

150

416

         }

151

152

         /**

153

          * Creates a new instance of <code>PropertiesConfigurationLayout</code>

154

          * and initializes it with the given configuration object. The data of the

155

          * specified layout object is copied.

156

          *

157

          * @param config the configuration (must not be <b>null</b>)

158

          * @param c the layout object to be copied

159

          */

160

         public PropertiesConfigurationLayout(PropertiesConfiguration config,

161

                 PropertiesConfigurationLayout c)

162

423

         {

163

423

             if (config == null)

164

             {

165

1

                 throw new IllegalArgumentException(

166

                         "Configuration must not be null!");

167

             }

168

422

             configuration = config;

169

422

             layoutData = new LinkedMap();

170

422

             config.addConfigurationListener(this);

171

172

422

             if (c != null)

173

             {

174

6

                 copyFrom(c);

175

             }

176

422

         }

177

178

         /**

179

          * Returns the associated configuration object.

180

          *

181

          * @return the associated configuration

182

          */

183

         public PropertiesConfiguration getConfiguration()

184

         {

185

21730

             return configuration;

186

         }

187

188

         /**

189

          * Returns the comment for the specified property key in a cononical form.

190

          * &quot;Canonical&quot; means that either all lines start with a comment

191

          * character or none. The <code>commentChar</code> parameter is <b>false</b>,

192

          * all comment characters are removed, so that the result is only the plain

193

          * text of the comment. Otherwise it is ensured that each line of the

194

          * comment starts with a comment character.

195

          *

196

          * @param key the key of the property

197

          * @param commentChar determines whether all lines should start with comment

198

          * characters or not

199

          * @return the canonical comment for this key (can be <b>null</b>)

200

          */

201

         public String getCanonicalComment(String key, boolean commentChar)

202

         {

203

40

             String comment = getComment(key);

204

40

             if (comment == null)

205

             {

206

1

                 return null;

207

             }

208

             else

209

             {

210

39

                 return trimComment(comment, commentChar);

211

             }

212

         }

213

214

         /**

215

          * Returns the comment for the specified property key. The comment is

216

          * returned as it was set (either manually by calling

217

          * <code>setComment()</code> or when it was loaded from a properties

218

          * file). No modifications are performed.

219

          *

220

          * @param key the key of the property

221

          * @return the comment for this key (can be <b>null</b>)

222

          */

223

         public String getComment(String key)

224

         {

225

293

             return fetchLayoutData(key).getComment();

226

         }

227

228

         /**

229

          * Sets the comment for the specified property key. The comment (or its

230

          * single lines if it is a multi-line comment) can start with a comment

231

          * character. If this is the case, it will be written without changes.

232

          * Otherwise a default comment character is added automatically.

233

          *

234

          * @param key the key of the property

235

          * @param comment the comment for this key (can be <b>null</b>, then the

236

          * comment will be removed)

237

          */

238

         public void setComment(String key, String comment)

239

         {

240

7

             fetchLayoutData(key).setComment(comment);

241

6

         }

242

243

         /**

244

          * Returns the number of blanc lines before this property key. If this key

245

          * does not exist, 0 will be returned.

246

          *

247

          * @param key the property key

248

          * @return the number of blanc lines before the property definition for this

249

          * key

250

          */

251

         public int getBlancLinesBefore(String key)

252

         {

253

351

             return fetchLayoutData(key).getBlancLines();

254

         }

255

256

         /**

257

          * Sets the number of blanc lines before the given property key. This can be

258

          * used for a logical grouping of properties.

259

          *

260

          * @param key the property key

261

          * @param number the number of blanc lines to add before this property

262

          * definition

263

          */

264

         public void setBlancLinesBefore(String key, int number)

265

         {

266

2

             fetchLayoutData(key).setBlancLines(number);

267

2

         }

268

269

         /**

270

          * Returns the header comment of the represented properties file in a

271

          * canonical form. With the <code>commentChar</code> parameter it can be

272

          * specified whether comment characters should be stripped or be always

273

          * present.

274

          *

275

          * @param commentChar determines the presence of comment characters

276

          * @return the header comment (can be <b>null</b>)

277

          */

278

         public String getCanonicalHeaderComment(boolean commentChar)

279

         {

280

11

             return (getHeaderComment() == null) ? null : trimComment(

281

                     getHeaderComment(), commentChar);

282

         }

283

284

         /**

285

          * Returns the header comment of the represented properties file. This

286

          * method returns the header comment exactly as it was set using

287

          * <code>setHeaderComment()</code> or extracted from the loaded properties

288

          * file.

289

          *

290

          * @return the header comment (can be <b>null</b>)

291

          */

292

         public String getHeaderComment()

293

         {

294

10769

             return headerComment;

295

         }

296

297

         /**

298

          * Sets the header comment for the represented properties file. This comment

299

          * will be output on top of the file.

300

          *

301

          * @param comment the comment

302

          */

303

         public void setHeaderComment(String comment)

304

         {

305

333

             headerComment = comment;

306

333

         }

307

308

         /**

309

          * Returns a flag whether the specified property is defined on a single

310

          * line. This is meaningful only if this property has multiple values.

311

          *

312

          * @param key the property key

313

          * @return a flag if this property is defined on a single line

314

          */

315

         public boolean isSingleLine(String key)

316

         {

317

253

             return fetchLayoutData(key).isSingleLine();

318

         }

319

320

         /**

321

          * Sets the &quot;single line flag&quot; for the specified property key.

322

          * This flag is evaluated if the property has multiple values (i.e. if it is

323

          * a list property). In this case, if the flag is set, all values will be

324

          * written in a single property definition using the list delimiter as

325

          * separator. Otherwise multiple lines will be written for this property,

326

          * each line containing one property value.

327

          *

328

          * @param key the property key

329

          * @param f the single line flag

330

          */

331

         public void setSingleLine(String key, boolean f)

332

         {

333

1

             fetchLayoutData(key).setSingleLine(f);

334

1

         }

335

336

         /**

337

          * Returns the &quot;force single line&quot; flag.

338

          *

339

          * @return the force single line flag

340

          * @see #setForceSingleLine(boolean)

341

          */

342

         public boolean isForceSingleLine()

343

         {

344

249

             return forceSingleLine;

345

         }

346

347

         /**

348

          * Sets the &quot;force single line&quot; flag. If this flag is set, all

349

          * properties with multiple values are written on single lines. This mode

350

          * provides more compatibility with <code>java.lang.Properties</code>,

351

          * which cannot deal with multiple definitions of a single property.

352

          *

353

          * @param f the force single line flag

354

          */

355

         public void setForceSingleLine(boolean f)

356

         {

357

1

             forceSingleLine = f;

358

1

         }

359

360

         /**

361

          * Returns a set with all property keys managed by this object.

362

          *

363

          * @return a set with all contained property keys

364

          */

365

         public Set getKeys()

366

         {

367

23

             return layoutData.keySet();

368

         }

369

370

         /**

371

          * Reads a properties file and stores its internal structure. The found

372

          * properties will be added to the associated configuration object.

373

          *

374

          * @param in the reader to the properties file

375

          * @throws ConfigurationException if an error occurs

376

          */

377

         public void load(Reader in) throws ConfigurationException

378

         {

379

833

             if (++loadCounter == 1)

380

             {

381

320

                 getConfiguration().removeConfigurationListener(this);

382

             }

383

833

             PropertiesConfiguration.PropertiesReader reader = new PropertiesConfiguration.PropertiesReader(

384

                     in, getConfiguration().getListDelimiter());

385

386

             try

387

             {

388

21393

                 while (reader.nextProperty())

389

                 {

390

19728

                     if (getConfiguration().propertyLoaded(reader.getPropertyName(),

391

                             reader.getPropertyValue()))

392

                     {

393

19376

                         boolean contained = layoutData.containsKey(reader

394

                                 .getPropertyName());

395

19376

                         int blancLines = 0;

396

19376

                         int idx = checkHeaderComment(reader.getCommentLines());

397

19376

                         while (idx < reader.getCommentLines().size()

398

                                 && ((String) reader.getCommentLines().get(idx))

399

26608

                                         .length() < 1)

400

                         {

401

7232

                             idx++;

402

7232

                             blancLines++;

403

                         }

404

19376

                         String comment = extractComment(reader.getCommentLines(),

405

                                 idx, reader.getCommentLines().size() - 1);

406

19376

                         PropertyLayoutData data = fetchLayoutData(reader

407

                                 .getPropertyName());

408

19376

                         if (contained)

409

                         {

410

10881

                             data.addComment(comment);

411

10881

                             data.setSingleLine(false);

412

                         }

413

                         else

414

                         {

415

8495

                             data.setComment(comment);

416

8495

                             data.setBlancLines(blancLines);

417

                         }

418

                     }

419

                 }

420

832

             }

421

             catch (IOException ioex)

422

             {

423

0

                 throw new ConfigurationException(ioex);

424

             }

425

             finally

426

             {

427

1

                 if (--loadCounter == 0)

428

                 {

429

320

                     getConfiguration().addConfigurationListener(this);

430

                 }

431

             }

432

832

         }

433

434

         /**

435

          * Writes the properties file to the given writer, preserving as much of its

436

          * structure as possible.

437

          *

438

          * @param out the writer

439

          * @throws ConfigurationException if an error occurs

440

          */

441

         public void save(Writer out) throws ConfigurationException

442

         {

443

             try

444

             {

445

30

                 PropertiesConfiguration.PropertiesWriter writer = new PropertiesConfiguration.PropertiesWriter(

446

                         out, getConfiguration().getListDelimiter());

447

30

                 if (headerComment != null)

448

                 {

449

5

                     writer.writeln(getCanonicalHeaderComment(true));

450

5

                     writer.writeln(null);

451

                 }

452

453

310

                 for (Iterator it = layoutData.keySet().iterator(); it.hasNext();)

454

                 {

455

250

                     String key = (String) it.next();

456

250

                     if (getConfiguration().containsKey(key))

457

                     {

458

459

                         // Output blanc lines before property

460

343

                         for (int i = 0; i < getBlancLinesBefore(key); i++)

461

                         {

462

95

                             writer.writeln(null);

463

                         }

464

465

                         // Output the comment

466

248

                         if (getComment(key) != null)

467

                         {

468

32

                             writer.writeln(getCanonicalComment(key, true));

469

                         }

470

471

                         // Output the property and its value

472

248

                         writer.writeProperty(key, getConfiguration().getProperty(

473

                                 key), isForceSingleLine() || isSingleLine(key));

474

                     }

475

                 }

476

30

                 writer.flush();

477

30

             }

478

             catch (IOException ioex)

479

             {

480

0

                 throw new ConfigurationException(ioex);

481

             }

482

30

         }

483

484

         /**

485

          * The event listener callback. Here event notifications of the

486

          * configuration object are processed to update the layout object properly.

487

          *

488

          * @param event the event object

489

          */

490

         public void configurationChanged(ConfigurationEvent event)

491

         {

492

249

             if (event.isBeforeUpdate())

493

             {

494

121

                 if (AbstractFileConfiguration.EVENT_RELOAD == event.getType())

495

                 {

496

6

                     clear();

497

                 }

498

             }

499

500

             else

501

             {

502

128

                 switch (event.getType())

503

                 {

504

                 case AbstractConfiguration.EVENT_ADD_PROPERTY:

505

78

                     boolean contained = layoutData.containsKey(event

506

                             .getPropertyName());

507

78

                     PropertyLayoutData data = fetchLayoutData(event

508

                             .getPropertyName());

509

78

                     data.setSingleLine(!contained);

510

78

                     break;

511

                 case AbstractConfiguration.EVENT_CLEAR_PROPERTY:

512

22

                     layoutData.remove(event.getPropertyName());

513

22

                     break;

514

                 case AbstractConfiguration.EVENT_CLEAR:

515

4

                     clear();

516

4

                     break;

517

                 case AbstractConfiguration.EVENT_SET_PROPERTY:

518

18

                     fetchLayoutData(event.getPropertyName());

519

                     break;

520

                 }

521

             }

522

249

         }

523

524

         /**

525

          * Returns a layout data object for the specified key. If this is a new key,

526

          * a new object is created and initialized with default values.

527

          *

528

          * @param key the key

529

          * @return the corresponding layout data object

530

          */

531

         private PropertyLayoutData fetchLayoutData(String key)

532

         {

533

20379

             if (key == null)

534

             {

535

1

                 throw new IllegalArgumentException("Property key must not be null!");

536

             }

537

538

20378

             PropertyLayoutData data = (PropertyLayoutData) layoutData.get(key);

539

20378

             if (data == null)

540

             {

541

8581

                 data = new PropertyLayoutData();

542

8581

                 data.setSingleLine(true);

543

8581

                 layoutData.put(key, data);

544

             }

545

546

20378

             return data;

547

         }

548

549

         /**

550

          * Removes all content from this layout object.

551

          */

552

         private void clear()

553

         {

554

10

             layoutData.clear();

555

10

             setHeaderComment(null);

556

10

         }

557

558

         /**

559

          * Tests whether a line is a comment, i.e. whether it starts with a comment

560

          * character.

561

          *

562

          * @param line the line

563

          * @return a flag if this is a comment line

564

          */

565

         static boolean isCommentLine(String line)

566

         {

567

144

             return PropertiesConfiguration.isCommentLine(line);

568

         }

569

570

         /**

571

          * Trims a comment. This method either removes all comment characters from

572

          * the given string, leaving only the plain comment text or ensures that

573

          * every line starts with a valid comment character.

574

          *

575

          * @param s the string to be processed

576

          * @param comment if <b>true</b>, a comment character will always be

577

          * enforced; if <b>false</b>, it will be removed

578

          * @return the trimmed comment

579

          */

580

         static String trimComment(String s, boolean comment)

581

         {

582

51

             StringBuffer buf = new StringBuffer(s.length());

583

51

             int lastPos = 0;

584

             int pos;

585

586

             do

587

             {

588

184

                 pos = s.indexOf(CR, lastPos);

589

184

                 if (pos >= 0)

590

                 {

591

133

                     String line = s.substring(lastPos, pos);

592

133

                     buf.append(stripCommentChar(line, comment)).append(CR);

593

133

                     lastPos = pos + CR.length();

594

                 }

595

184

             } while (pos >= 0);

596

597

51

             if (lastPos < s.length())

598

             {

599

24

                 buf.append(stripCommentChar(s.substring(lastPos), comment));

600

             }

601

51

             return buf.toString();

602

         }

603

604

         /**

605

          * Either removes the comment character from the given comment line or

606

          * ensures that the line starts with a comment character.

607

          *

608

          * @param s the comment line

609

          * @param comment if <b>true</b>, a comment character will always be

610

          * enforced; if <b>false</b>, it will be removed

611

          * @return the line without comment character

612

          */

613

         static String stripCommentChar(String s, boolean comment)

614

         {

615

157

             if (s.length() < 1 || (isCommentLine(s) == comment))

616

             {

617

129

                 return s;

618

             }

619

620

             else

621

             {

622

28

                 if (!comment)

623

                 {

624

20

                     int pos = 0;

625

                     // find first comment character

626

20

                     while (PropertiesConfiguration.COMMENT_CHARS.indexOf(s

627

23

                             .charAt(pos)) < 0)

628

                     {

629

3

                         pos++;

630

                     }

631

632

                     // Remove leading spaces

633

20

                     pos++;

634

20

                     while (pos < s.length()

635

39

                             && Character.isWhitespace(s.charAt(pos)))

636

                     {

637

19

                         pos++;

638

                     }

639

640

20

                     return (pos < s.length()) ? s.substring(pos)

641

                             : StringUtils.EMPTY;

642

                 }

643

                 else

644

                 {

645

8

                     return COMMENT_PREFIX + s;

646

                 }

647

             }

648

         }

649

650

         /**

651

          * Extracts a comment string from the given range of the specified comment

652

          * lines. The single lines are added using a line feed as separator.

653

          *

654

          * @param commentLines a list with comment lines

655

          * @param from the start index

656

          * @param to the end index (inclusive)

657

          * @return the comment string (<b>null</b> if it is undefined)

658

          */

659

         private String extractComment(List commentLines, int from, int to)

660

         {

661

19685

             if (to < from)

662

             {

663

17785

                 return null;

664

             }

665

666

             else

667

             {

668

1900

                 StringBuffer buf = new StringBuffer((String) commentLines.get(from));

669

9883

                 for (int i = from + 1; i <= to; i++)

670

                 {

671

7983

                     buf.append(CR);

672

7983

                     buf.append(commentLines.get(i));

673

                 }

674

1900

                 return buf.toString();

675

             }

676

         }

677

678

         /**

679

          * Checks if parts of the passed in comment can be used as header comment.

680

          * This method checks whether a header comment can be defined (i.e. whether

681

          * this is the first comment in the loaded file). If this is the case, it is

682

          * searched for the lates blanc line. This line will mark the end of the

683

          * header comment. The return value is the index of the first line in the

684

          * passed in list, which does not belong to the header comment.

685

          *

686

          * @param commentLines the comment lines

687

          * @return the index of the next line after the header comment

688

          */

689

         private int checkHeaderComment(List commentLines)

690

         {

691

19376

             if (loadCounter == 1 && getHeaderComment() == null

692

                     && layoutData.isEmpty())

693

             {

694

                 // This is the first comment. Search for blanc lines.

695

309

                 int index = commentLines.size() - 1;

696

309

                 while (index >= 0

697

317

                         && ((String) commentLines.get(index)).length() > 0)

698

                 {

699

8

                     index--;

700

                 }

701

309

                 setHeaderComment(extractComment(commentLines, 0, index - 1));

702

309

                 return index + 1;

703

             }

704

             else

705

             {

706

19067

                 return 0;

707

             }

708

         }

709

710

         /**

711

          * Copies the data from the given layout object.

712

          *

713

          * @param c the layout object to copy

714

          */

715

         private void copyFrom(PropertiesConfigurationLayout c)

716

         {

717

59

             for (Iterator it = c.getKeys().iterator(); it.hasNext();)

718

             {

719

47

                 String key = (String) it.next();

720

47

                 PropertyLayoutData data = (PropertyLayoutData) c.layoutData

721

                         .get(key);

722

47

                 layoutData.put(key, data.clone());

723

             }

724

6

         }

725

726

         /**

727

          * A helper class for storing all layout related information for a

728

          * configuration property.

729

          */

730

         static class PropertyLayoutData implements Cloneable

731

         {

732

             /** Stores the comment for the property. */

733

             private StringBuffer comment;

734

735

             /** Stores the number of blanc lines before this property. */

736

             private int blancLines;

737

738

             /** Stores the single line property. */

739

             private boolean singleLine;

740

741

             /**

742

              * Creates a new instance of <code>PropertyLayoutData</code>.

743

              */

744

             public PropertyLayoutData()

745

8581

             {

746

8581

                 singleLine = true;

747

8581

             }

748

749

             /**

750

              * Returns the number of blanc lines before this property.

751

              *

752

              * @return the number of blanc lines before this property

753

              */

754

             public int getBlancLines()

755

             {

756

351

                 return blancLines;

757

             }

758

759

             /**

760

              * Sets the number of properties before this property.

761

              *

762

              * @param blancLines the number of properties before this property

763

              */

764

             public void setBlancLines(int blancLines)

765

             {

766

8497

                 this.blancLines = blancLines;

767

8497

             }

768

769

             /**

770

              * Returns the single line flag.

771

              *

772

              * @return the single line flag

773

              */

774

             public boolean isSingleLine()

775

             {

776

253

                 return singleLine;

777

             }

778

779

             /**

780

              * Sets the single line flag.

781

              *

782

              * @param singleLine the single line flag

783

              */

784

             public void setSingleLine(boolean singleLine)

785

             {

786

19541

                 this.singleLine = singleLine;

787

19541

             }

788

789

             /**

790

              * Adds a comment for this property. If already a comment exists, the

791

              * new comment is added (separated by a newline).

792

              *

793

              * @param s the comment to add

794

              */

795

             public void addComment(String s)

796

             {

797

10881

                 if (s != null)

798

                 {

799

829

                     if (comment == null)

800

                     {

801

16

                         comment = new StringBuffer(s);

802

                     }

803

                     else

804

                     {

805

813

                         comment.append(CR).append(s);

806

                     }

807

                 }

808

10881

             }

809

810

             /**

811

              * Sets the comment for this property.

812

              *

813

              * @param s the new comment (can be <b>null</b>)

814

              */

815

             public void setComment(String s)

816

             {

817

8501

                 if (s == null)

818

                 {

819

7450

                     comment = null;

820

                 }

821

                 else

822

                 {

823

1051

                     comment = new StringBuffer(s);

824

                 }

825

8501

             }

826

827

             /**

828

              * Returns the comment for this property. The comment is returned as it

829

              * is, without processing of comment characters.

830

              *

831

              * @return the comment (can be <b>null</b>)

832

              */

833

             public String getComment()

834

             {

835

300

                 return (comment == null) ? null : comment.toString();

836

             }

837

838

             /**

839

              * Creates a copy of this object.

840

              *

841

              * @return the copy

842

              */

843

             public Object clone()

844

             {

845

                 try

846

                 {

847

47

                     PropertyLayoutData copy = (PropertyLayoutData) super.clone();

848

47

                     if (comment != null)

849

                     {

850

                         // must copy string buffer, too

851

7

                         copy.comment = new StringBuffer(getComment());

852

                     }

853

47

                     return copy;

854

                 }

855

                 catch (CloneNotSupportedException cnex)

856

                 {

857

                     // This cannot happen!

858

0

                     throw new ConfigurationRuntimeException(cnex);

859

                 }

860

             }

861

         }

862

     }