Context:
/*
 * Copyright (C) 2007 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.throwingproviders;

import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.AbstractModule;
import com.google.inject.BindingAnnotation;
import com.google.inject.CreationException;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.Message;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.rmi.AccessException;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.TooManyListenersException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
 * @author jmourits@google.com (Jerome Mourits)
 * @author jessewilson@google.com (Jesse Wilson)
 */
@SuppressWarnings("deprecation")
@RunWith(JUnit4.class)
public class ThrowingProviderTest {
  @Target(METHOD)
  @Retention(RUNTIME)
  @BindingAnnotation
  @interface NotExceptionScoping {};

  private final TypeLiteral<RemoteProvider<String>> remoteProviderOfString =
      new TypeLiteral<RemoteProvider<String>>() {};
  private final MockRemoteProvider<String> mockRemoteProvider = new MockRemoteProvider<>();
  private final TestScope testScope = new TestScope();
  private Injector bindInjector =
      Guice.createInjector(
          new AbstractModule() {
            @Override
            protected void configure() {
              ThrowingProviderBinder.create(binder())
                  .bind(RemoteProvider.class, String.class)
                  .to(mockRemoteProvider)
                  .in(testScope);

              ThrowingProviderBinder.create(binder())
                  .bind(RemoteProvider.class, String.class)
                  .annotatedWith(NotExceptionScoping.class)
                  .scopeExceptions(false)
                  .to(mockRemoteProvider)
                  .in(testScope);
            }
          });
  private Injector providesInjector =
      Guice.createInjector(
          new AbstractModule() {
            @Override
            protected void configure() {
              install(ThrowingProviderBinder.forModule(this));
              bindScope(TestScope.Scoped.class, testScope);
            }

            @SuppressWarnings("unused")
            @CheckedProvides(RemoteProvider.class)
            @TestScope.Scoped
            String throwOrGet() throws RemoteException {
              return mockRemoteProvider.get();
            }

            @SuppressWarnings("unused")
            @CheckedProvides(value = RemoteProvider.class, scopeExceptions = false)
            @NotExceptionScoping
            @TestScope.Scoped
            String notExceptionScopingThrowOrGet() throws RemoteException {
              return mockRemoteProvider.get();
            }
          });

  @Test
  public void testExceptionsThrown_Bind() {
    tExceptionsThrown(bindInjector);
  }

  @Test
  public void testExceptionsThrown_Provides() {
    tExceptionsThrown(providesInjector);
  }

  private void tExceptionsThrown(Injector injector) {
    RemoteProvider<String> remoteProvider = injector.getInstance(Key.get(remoteProviderOfString));

    mockRemoteProvider.throwOnNextGet("kaboom!");
    try {
      remoteProvider.get();
      fail();
    } catch (RemoteException expected) {
      assertEquals("kaboom!", expected.getMessage());
    }
  }

  @Test
  public void testValuesScoped_Bind() throws RemoteException {
    tValuesScoped(bindInjector, null);
  }

  @Test
  public void testValuesScoped_Provides() throws RemoteException {
    tValuesScoped(providesInjector, null);
  }

  @Test
  public void testValuesScopedWhenNotExceptionScoping_Bind() throws RemoteException {
    tValuesScoped(bindInjector, NotExceptionScoping.class);
  }

  @Test
  public void testValuesScopedWhenNotExceptionScoping_Provides() throws RemoteException {
    tValuesScoped(providesInjector, NotExceptionScoping.class);
  }

  private void tValuesScoped(Injector injector, Class<? extends Annotation> annotation)
      throws RemoteException {
    Key<RemoteProvider<String>> key =
        annotation != null
            ? Key.get(remoteProviderOfString, annotation)
            : Key.get(remoteProviderOfString);
    RemoteProvider<String> remoteProvider = injector.getInstance(key);

    mockRemoteProvider.setNextToReturn("A");
    assertEquals("A", remoteProvider.get());

    mockRemoteProvider.setNextToReturn("B");
    assertEquals("A", remoteProvider.get());

    testScope.beginNewScope();
    assertEquals("B", remoteProvider.get());
  }

  @Test
  public void testExceptionsScoped_Bind() {
    tExceptionsScoped(bindInjector);
  }

  @Test
  public void testExceptionsScoped_Provides() {
    tExceptionsScoped(providesInjector);
  }

  private void tExceptionsScoped(Injector injector) {
    RemoteProvider<String> remoteProvider = injector.getInstance(Key.get(remoteProviderOfString));

    mockRemoteProvider.throwOnNextGet("A");
    try {
      remoteProvider.get();
      fail();
    } catch (RemoteException expected) {
      assertEquals("A", expected.getMessage());
    }

    mockRemoteProvider.throwOnNextGet("B");
    try {
      remoteProvider.get();
      fail();
    } catch (RemoteException expected) {
      assertEquals("A", expected.getMessage());
    }
  }

  @Test
  public void testExceptionsNotScopedWhenNotExceptionScoping_Bind() {
    tExceptionsNotScopedWhenNotExceptionScoping(bindInjector);
  }

  @Test
  public void testExceptionsNotScopedWhenNotExceptionScoping_Provides() {
    tExceptionsNotScopedWhenNotExceptionScoping(providesInjector);
  }

  private void tExceptionsNotScopedWhenNotExceptionScoping(Injector injector) {
    RemoteProvider<String> remoteProvider =
        injector.getInstance(Key.get(remoteProviderOfString, NotExceptionScoping.class));

    mockRemoteProvider.throwOnNextGet("A");
    try {
      remoteProvider.get();
      fail();
    } catch (RemoteException expected) {
      assertEquals("A", expected.getMessage());
    }

    mockRemoteProvider.throwOnNextGet("B");
    try {
      remoteProvider.get();
      fail();
    } catch (RemoteException expected) {
      assertEquals("B", expected.getMessage());
    }
  }

  @Test
  public void testAnnotations_Bind() throws RemoteException {
    final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<>();
    final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<>();
    bindInjector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                ThrowingProviderBinder.create(binder())
                    .bind(RemoteProvider.class, String.class)
                    .annotatedWith(Names.named("a"))
                    .to(mockRemoteProviderA);

                ThrowingProviderBinder.create(binder())
                    .bind(RemoteProvider.class, String.class)
                    .to(mockRemoteProviderB);
              }
            });
    tAnnotations(bindInjector, mockRemoteProviderA, mockRemoteProviderB);
  }

  @Test
  public void testAnnotations_Provides() throws RemoteException {
    final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<>();
    final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<>();
    providesInjector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                install(ThrowingProviderBinder.forModule(this));
              }

              @SuppressWarnings("unused")
              @CheckedProvides(RemoteProvider.class)
              @Named("a")
              String throwOrGet() throws RemoteException {
                return mockRemoteProviderA.get();
              }

              @SuppressWarnings("unused")
              @CheckedProvides(RemoteProvider.class)
              String throwOrGet2() throws RemoteException {
                return mockRemoteProviderB.get();
              }
            });
    tAnnotations(providesInjector, mockRemoteProviderA, mockRemoteProviderB);
  }

  private void tAnnotations(
      Injector injector, MockRemoteProvider<String> mockA, MockRemoteProvider<String> mockB)
      throws RemoteException {
    mockA.setNextToReturn("A");
    mockB.setNextToReturn("B");
    assertEquals(
        "A", injector.getInstance(Key.get(remoteProviderOfString, Names.named("a"))).get());

    assertEquals("B", injector.getInstance(Key.get(remoteProviderOfString)).get());
  }

  @Test
  public void testUndeclaredExceptions_Bind() throws RemoteException {
    tUndeclaredExceptions(bindInjector);
  }

  @Test
  public void testUndeclaredExceptions_Provides() throws RemoteException {
    tUndeclaredExceptions(providesInjector);
  }

  private void tUndeclaredExceptions(Injector injector) throws RemoteException {
    RemoteProvider<String> remoteProvider = injector.getInstance(Key.get(remoteProviderOfString));
    mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("A"));
    try {
      remoteProvider.get();
      fail();
    } catch (RuntimeException e) {
      assertEquals("A", e.getCause().getMessage());
    }

    // undeclared exceptions shouldn't be scoped
    mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("B"));
    try {
      remoteProvider.get();
      fail();
    } catch (RuntimeException e) {
      assertEquals("B", e.getCause().getMessage());
    }
  }

  @Test
  public void testThrowingProviderSubclassing() throws RemoteException {
    final SubMockRemoteProvider aProvider = new SubMockRemoteProvider();
    aProvider.setNextToReturn("A");

    bindInjector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                ThrowingProviderBinder.create(binder())
                    .bind(RemoteProvider.class, String.class)
                    .to(aProvider);
              }
            });

    assertEquals("A", bindInjector.getInstance(Key.get(remoteProviderOfString)).get());
  }

  static class SubMockRemoteProvider extends MockRemoteProvider<String> {}

  @Test
  public void testBindingToNonInterfaceType_Bind() throws RemoteException {
    try {
      Guice.createInjector(
          new AbstractModule() {
            @Override
            protected void configure() {
              ThrowingProviderBinder.create(binder())
                  .bind(MockRemoteProvider.class, String.class)
                  .to(mockRemoteProvider);
            }
          });
      fail();
    } catch (CreationException expected) {
      assertEquals(
          MockRemoteProvider.class.getName() + " must be an interface",
          Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    }
  }

  @Test
  public void testBindingToNonInterfaceType_Provides() throws RemoteException {
    try {
      Guice.createInjector(
          new AbstractModule() {
            @Override
            protected void configure() {
              install(ThrowingProviderBinder.forModule(this));
            }

            @SuppressWarnings("unused")
            @CheckedProvides(MockRemoteProvider.class)
            String foo() {
              return null;
            }
          });
      fail();
    } catch (CreationException expected) {
      assertEquals(
          MockRemoteProvider.class.getName() + " must be an interface",
          Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    }
  }

  @Test
  public void testBindingToSubSubInterface_Bind() throws RemoteException {
    try {
      bindInjector =
          Guice.createInjector(
              new AbstractModule() {
                @Override
                protected void configure() {
                  ThrowingProviderBinder.create(binder())
                      .bind(SubRemoteProvider.class, String.class);
                }
              });
      fail();
    } catch (CreationException expected) {
      assertEquals(
          SubRemoteProvider.class.getName()
              + " must extend CheckedProvider (and only CheckedProvider)",
          Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    }
  }

  @Test
  public void testBindingToSubSubInterface_Provides() throws RemoteException {
    try {
      Guice.createInjector(
          new AbstractModule() {
            @Override
            protected void configure() {
              install(ThrowingProviderBinder.forModule(this));
            }

            @SuppressWarnings("unused")
            @CheckedProvides(SubRemoteProvider.class)
            String foo() {
              return null;
            }
          });
      fail();
    } catch (CreationException expected) {
      assertEquals(
          SubRemoteProvider.class.getName()
              + " must extend CheckedProvider (and only CheckedProvider)",
          Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    }
  }

  interface SubRemoteProvider extends RemoteProvider<String> {}

  @Test
  public void testBindingToInterfaceWithExtraMethod_Bind() throws RemoteException {
    try {
      bindInjector =
          Guice.createInjector(
              new AbstractModule() {
                @Override
                protected void configure() {
                  ThrowingProviderBinder.create(binder())
                      .bind(RemoteProviderWithExtraMethod.class, String.class);
                }
              });
      fail();
    } catch (CreationException expected) {
      assertEquals(
          RemoteProviderWithExtraMethod.class.getName()
              + " may not declare any new methods, but declared "
              + RemoteProviderWithExtraMethod.class.getDeclaredMethods()[0].toGenericString(),
          Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    }
  }

  @Test
  public void testBindingToInterfaceWithExtraMethod_Provides() throws RemoteException {
    try {
      Guice.createInjector(
          new AbstractModule() {
            @Override
            protected void configure() {
              install(ThrowingProviderBinder.forModule(this));
            }

            @SuppressWarnings("unused")
            @CheckedProvides(RemoteProviderWithExtraMethod.class)
            String foo() {
              return null;
            }
          });
      fail();
    } catch (CreationException expected) {
      assertEquals(
          RemoteProviderWithExtraMethod.class.getName()
              + " may not declare any new methods, but declared "
              + RemoteProviderWithExtraMethod.class.getDeclaredMethods()[0].toGenericString(),
          Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    }
  }

  @Test
  public void testDependencies_Bind() {
    bindInjector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                bind(String.class).toInstance("Foo");
                bind(Integer.class).toInstance(5);
                bind(Double.class).toInstance(5d);
                bind(Long.class).toInstance(5L);
                ThrowingProviderBinder.create(binder())
                    .bind(RemoteProvider.class, String.class)
                    .to(DependentRemoteProvider.class);
              }
            });

    HasDependencies hasDependencies =
        (HasDependencies) bindInjector.getBinding(Key.get(remoteProviderOfString));
    hasDependencies =
        (HasDependencies)
            bindInjector.getBinding(
                Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
    // Make sure that that is dependent on DependentRemoteProvider.
    assertEquals(
        Dependency.get(Key.get(DependentRemoteProvider.class)),
        Iterables.getOnlyElement(hasDependencies.getDependencies()));
    // And make sure DependentRemoteProvider has the proper dependencies.
    hasDependencies = (HasDependencies) bindInjector.getBinding(DependentRemoteProvider.class);
    Set<Key<?>> dependencyKeys =
        hasDependencies.getDependencies().stream()
            .map(Dependency::getKey)
            .collect(toImmutableSet());
    assertEquals(
        ImmutableSet.<Key<?>>of(
            Key.get(String.class),
            Key.get(Integer.class),
            Key.get(Long.class),
            Key.get(Double.class)),
        dependencyKeys);
  }

  @Test
  public void testDependencies_Provides() {
    providesInjector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                bind(String.class).toInstance("Foo");
                bind(Integer.class).toInstance(5);
                bind(Double.class).toInstance(5d);
                bind(Long.class).toInstance(5L);
                install(ThrowingProviderBinder.forModule(this));
              }

              @SuppressWarnings("unused")
              @CheckedProvides(RemoteProvider.class)
              String foo(String s, Integer i, Double d, Long l) {
                return null;
              }
            });

    HasDependencies hasDependencies =
        (HasDependencies) providesInjector.getBinding(Key.get(remoteProviderOfString));
    // RemoteProvider<String> is dependent on the provider method..
    hasDependencies =
        (HasDependencies)
            providesInjector.getBinding(
                Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
    // And the provider method has our real dependencies..
    hasDependencies =
        (HasDependencies)
            providesInjector.getBinding(
                Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
    Set<Key<?>> dependencyKeys =
        hasDependencies.getDependencies().stream()
            .map(Dependency::getKey)
            .collect(toImmutableSet());
    assertEquals(
        ImmutableSet.<Key<?>>of(
            Key.get(String.class),
            Key.get(Integer.class),
            Key.get(Long.class),
            Key.get(Double.class)),
        dependencyKeys);
  }

  interface RemoteProviderWithExtraMethod<T> extends ThrowingProvider<T, RemoteException> {
    T get(T defaultValue) throws RemoteException;
  }

  interface RemoteProvider<T> extends ThrowingProvider<T, RemoteException> {}

  static class DependentRemoteProvider<T> implements RemoteProvider<T> {
    @Inject double foo;

    @Inject
    public DependentRemoteProvider(String foo, int bar) {}

    @Inject
    void initialize(long foo) {}

    @Override
    public T get() throws RemoteException {
      return null;
    }
  }

  static class MockRemoteProvider<T> implements RemoteProvider<T> {
    Exception nextToThrow;
    T nextToReturn;

    public void throwOnNextGet(String message) {
      throwOnNextGet(new RemoteException(message));
    }

    public void throwOnNextGet(Exception nextToThrow) {
      this.nextToThrow = nextToThrow;
    }

    public void setNextToReturn(T nextToReturn) {
      this.nextToReturn = nextToReturn;
    }

    @Override
    public T get() throws RemoteException {
      if (nextToThrow instanceof RemoteException) {
        throw (RemoteException) nextToThrow;
      } else if (nextToThrow instanceof RuntimeException) {
        throw (RuntimeException) nextToThrow;
      } else if (nextToThrow == null) {
        return nextToReturn;
      } else {
        throw new AssertionError("nextToThrow must be a runtime or remote exception");
      }
    }
  }

  @Test
  public void testBindingToInterfaceWithBoundValueType_Bind() throws RemoteException {
    bindInjector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                ThrowingProviderBinder.create(binder())
                    .bind(StringRemoteProvider.class, String.class)
                    .to(
                        new StringRemoteProvider() {
                          @Override
                          public String get() throws RemoteException {
                            return "A";
                          }
                        });
              }
            });

    assertEquals("A", bindInjector.getInstance(StringRemoteProvider.class).get());
  }

  @Test
  public void testBindingToInterfaceWithBoundValueType_Provides() throws RemoteException {
    providesInjector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                install(ThrowingProviderBinder.forModule(this));
              }

              @SuppressWarnings("unused")
              @CheckedProvides(StringRemoteProvider.class)
              String foo() throws RemoteException {
                return "A";
              }
            });

    assertEquals("A", providesInjector.getInstance(StringRemoteProvider.class).get());
  }

  interface StringRemoteProvider extends ThrowingProvider<String, RemoteException> {}

  @Test
  public void testBindingToInterfaceWithGeneric_Bind() throws RemoteException {
    bindInjector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                ThrowingProviderBinder.create(binder())
                    .bind(RemoteProvider.class, new TypeLiteral<List<String>>() {}.getType())
                    .to(
                        new RemoteProvider<List<String>>() {
                          @Override
                          public List<String> get() throws RemoteException {
                            return Arrays.asList("A", "B");
                          }
                        });
              }
            });

    Key<RemoteProvider<List<String>>> key =
        Key.get(new TypeLiteral<RemoteProvider<List<String>>>() {});
    assertEquals(Arrays.asList("A", "B"), bindInjector.getInstance(key).get());
  }

  @Test
  public void testBindingToInterfaceWithGeneric_Provides() throws RemoteException {
    providesInjector =
        Guice.createInjector(
            new AbstractModule() {
              @Override
              protected void configure() {
                install(ThrowingProviderBinder.forModule(this));
              }

              @SuppressWarnings("unused")
              @CheckedProvides(RemoteProvider.class)
              List<String> foo() throws RemoteException {
                return Arrays.asList("A", "B");
              }
            });

    Key<RemoteProvider<List<String>>> key =
        Key.get(new TypeLiteral<RemoteProvider<List<String>>>() {});
    assertEquals(Arrays.asList("A", "B"), providesInjector.getInstance(key).get());
  }

  @Test
  public void testProviderMethodWithWrongException() {
    try {
      Guice.createInjector(
          new AbstractModule() {
            @Override
            protected void configure() {
              install(ThrowingProviderBinder.forModule(this));
            }

            @SuppressWarnings("unused")
            @CheckedProvides(RemoteProvider.class)
            String foo() throws InterruptedException {
              return null;
            }
          });
      fail();
    } catch (CreationException ce) {
      assertEquals(
          InterruptedException.class.getName()
              + " is not compatible with the exceptions (["
              + RemoteException.class
              + "]) declared in the CheckedProvider interface ("
              + RemoteProvider.class.getName()
              + ")",

Answer: Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());

<think>
The test ends with a catch block for CreationException where an assertEquals is started to compare the expected error message string with the actual message from the exception. Following the pattern used elsewhere, the second argument should retrieve the single error message from the CreationException via Iterables.getOnlyElement(ce.getErrorMessages()).getMessage(). This completes the assertEquals call.
</think>
Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());


Context:
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.smartdata.hdfs.metric.fetcher;

import org.apache.hadoop.fs.XAttr;
import org.apache.hadoop.hdfs.DFSClient;
import org.apache.hadoop.hdfs.inotify.Event;
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
import org.apache.hadoop.io.WritableUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smartdata.conf.SmartConf;
import org.smartdata.hdfs.CompatibilityHelperLoader;
import org.smartdata.hdfs.HadoopUtil;
import org.smartdata.metastore.DBType;
import org.smartdata.metastore.MetaStore;
import org.smartdata.metastore.MetaStoreException;
import org.smartdata.model.BackUpInfo;
import org.smartdata.model.FileDiff;
import org.smartdata.model.FileDiffType;
import org.smartdata.model.FileInfo;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * This is a very preliminary and buggy applier, can further enhance by referring to
 * {@link org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader}
 */
public class InotifyEventApplier {
  private final MetaStore metaStore;
  private DFSClient client;
  private static final Logger LOG =
      LoggerFactory.getLogger(InotifyEventFetcher.class);
  private List<String> ignoreEventDirs;
  private List<String> fetchEventDirs;
  private NamespaceFetcher namespaceFetcher;

  public InotifyEventApplier(MetaStore metaStore, DFSClient client) {
    this.metaStore = metaStore;
    this.client = client;
    initialize();
  }

  public InotifyEventApplier(MetaStore metaStore, DFSClient client, NamespaceFetcher namespaceFetcher) {
    this(metaStore, client);
    this.namespaceFetcher = namespaceFetcher;
  }

  private void initialize(){
    SmartConf conf = new SmartConf();
    ignoreEventDirs = conf.getIgnoreDir();
    fetchEventDirs = conf.getCoverDir();
  }

  public void apply(List<Event> events) throws IOException, MetaStoreException, InterruptedException {
    List<String> statements = new ArrayList<>();
    for (Event event : events) {
      List<String> gen = getSqlStatement(event);
      if (gen != null && !gen.isEmpty()) {
        for (String s : gen) {
          if (s != null && s.length() > 0) {
            statements.add(s);
          }
        }
      }
    }
    this.metaStore.execute(statements);
  }

  //check if the dir is in ignoreList

  public void apply(Event[] events) throws IOException, MetaStoreException, InterruptedException {
    this.apply(Arrays.asList(events));
  }

  private boolean shouldIgnore(String path) {
    String toCheck = path.endsWith("/") ? path : path + "/";
    for (String s : ignoreEventDirs) {
      if (toCheck.startsWith(s)) {
        return true;
      }
    }
    if (fetchEventDirs.isEmpty()) {
      return false;
    }
    for (String s : fetchEventDirs) {
      if (toCheck.startsWith(s)) {
        return false;
      }
    }
    return true;
  }

  private List<String> getSqlStatement(Event event) throws IOException, MetaStoreException, InterruptedException {
    String path;
    String srcPath, dstPath;
    LOG.debug("Even Type = {}", event.getEventType().toString());
    switch (event.getEventType()) {
      case CREATE:
        path = ((Event.CreateEvent) event).getPath();
        if (shouldIgnore(path)) {
          return Arrays.asList();
        }
        LOG.trace("event type:" + event.getEventType().name() +
            ", path:" + ((Event.CreateEvent) event).getPath());
        return Arrays.asList(this.getCreateSql((Event.CreateEvent) event));
      case CLOSE:
        path = ((Event.CloseEvent) event).getPath();
        if (shouldIgnore(path)) {
          return Arrays.asList();
        }
        LOG.trace("event type:" + event.getEventType().name() +
            ", path:" + ((Event.CloseEvent) event).getPath());
        return Arrays.asList(this.getCloseSql((Event.CloseEvent) event));
      case RENAME:
        srcPath = ((Event.RenameEvent) event).getSrcPath();
        dstPath = ((Event.RenameEvent) event).getDstPath();
        if (shouldIgnore(srcPath) && shouldIgnore(dstPath)) {
          return Arrays.asList();
        }
        LOG.trace("event type:" + event.getEventType().name() +
            ", src path:" + ((Event.RenameEvent) event).getSrcPath() +
            ", dest path:" + ((Event.RenameEvent) event).getDstPath());
        return this.getRenameSql((Event.RenameEvent)event);
      case METADATA:
        path = ((Event.MetadataUpdateEvent)event).getPath();
        if (shouldIgnore(path)) {
          return Arrays.asList();
        }
        LOG.trace("event type:" + event.getEventType().name() +
            ", path:" + ((Event.MetadataUpdateEvent)event).getPath());
        return Arrays.asList(this.getMetaDataUpdateSql((Event.MetadataUpdateEvent)event));
      case APPEND:
        path = ((Event.AppendEvent)event).getPath();
        if (shouldIgnore(path)) {
          return Arrays.asList();
        }
        LOG.trace("event type:" + event.getEventType().name() +
            ", path:" + ((Event.AppendEvent)event).getPath());
        return this.getAppendSql((Event.AppendEvent)event);
      case UNLINK:
        path = ((Event.UnlinkEvent)event).getPath();
        if (shouldIgnore(path)) {
          return Arrays.asList();
        }
        LOG.trace("event type:" + event.getEventType().name() +
            ", path:" + ((Event.UnlinkEvent)event).getPath());
        return this.getUnlinkSql((Event.UnlinkEvent)event);
    }
    return Arrays.asList();
  }

  //Todo: times and ec policy id, etc.
  private String getCreateSql(Event.CreateEvent createEvent) throws IOException, MetaStoreException {
    HdfsFileStatus fileStatus = client.getFileInfo(createEvent.getPath());
    if (fileStatus == null) {
      LOG.debug("Can not get HdfsFileStatus for file " + createEvent.getPath());
      return "";
    }
    FileInfo fileInfo = HadoopUtil.convertFileStatus(fileStatus, createEvent.getPath());

    if (inBackup(fileInfo.getPath())) {
      if (!fileInfo.isdir()) {

        // ignore dir
        FileDiff fileDiff = new FileDiff(FileDiffType.APPEND);
        fileDiff.setSrc(fileInfo.getPath());
        fileDiff.getParameters().put("-offset", String.valueOf(0));
        // Note that "-length 0" means create an empty file
        fileDiff.getParameters()
            .put("-length", String.valueOf(fileInfo.getLength()));
        // TODO add support in CopyFileAction or split into two file diffs
        //add modification_time and access_time to filediff
        fileDiff.getParameters().put("-mtime", "" + fileInfo.getModificationTime());
        // fileDiff.getParameters().put("-atime", "" + fileInfo.getAccessTime());
        //add owner to filediff
        fileDiff.getParameters().put("-owner", "" + fileInfo.getOwner());
        fileDiff.getParameters().put("-group", "" + fileInfo.getGroup());
        //add Permission to filediff
        fileDiff.getParameters().put("-permission", "" + fileInfo.getPermission());
        //add replication count to file diff
        fileDiff.getParameters().put("-replication", "" + fileInfo.getBlockReplication());
        metaStore.insertFileDiff(fileDiff);
      }
    }
    metaStore.deleteFileByPath(fileInfo.getPath());
    metaStore.deleteFileState(fileInfo.getPath());
    metaStore.insertFile(fileInfo);
    return "";
  }

  private boolean inBackup(String src) throws MetaStoreException {
    if (metaStore.srcInbackup(src)) {
      return true;
    }
    return false;
  }

  //Todo: should update mtime? atime?
  private String getCloseSql(Event.CloseEvent closeEvent) throws IOException, MetaStoreException {
    FileDiff fileDiff = new FileDiff(FileDiffType.APPEND);
    fileDiff.setSrc(closeEvent.getPath());
    long newLen = closeEvent.getFileSize();
    long currLen = 0l;
    // TODO make sure offset is correct
    if (inBackup(closeEvent.getPath())) {
      FileInfo fileInfo = metaStore.getFile(closeEvent.getPath());
      if (fileInfo == null) {
        // TODO add metadata
        currLen = 0;
      } else {
        currLen = fileInfo.getLength();
      }
      if (currLen != newLen) {
        fileDiff.getParameters().put("-offset", String.valueOf(currLen));
        fileDiff.getParameters()
            .put("-length", String.valueOf(newLen - currLen));
        metaStore.insertFileDiff(fileDiff);
      }
    }
    return String.format(
        "UPDATE file SET length = %s, modification_time = %s WHERE path = '%s';",
        closeEvent.getFileSize(), closeEvent.getTimestamp(), closeEvent.getPath());
  }

  //Todo: should update mtime? atime?
//  private String getTruncateSql(Event.TruncateEvent truncateEvent) {
//    return String.format(
//        "UPDATE file SET length = %s, modification_time = %s WHERE path = '%s';",
//        truncateEvent.getFileSize(), truncateEvent.getTimestamp(), truncateEvent.getPath());
//  }

  private List<String> getRenameSql(Event.RenameEvent renameEvent)
      throws IOException, MetaStoreException, InterruptedException {
    String src = renameEvent.getSrcPath();
    String dest = renameEvent.getDstPath();
    List<String> ret = new ArrayList<>();
    HdfsFileStatus status = client.getFileInfo(dest);
    FileInfo info = metaStore.getFile(src);
    // TODO: consider src or dest is ignored by SSM
    if (inBackup(src)) {
      // rename the file if the renamed file is still under the backup src dir
      // if not, insert a delete file diff
      if (inBackup(dest)) {
        FileDiff fileDiff = new FileDiff(FileDiffType.RENAME);
        fileDiff.setSrc(src);
        fileDiff.getParameters().put("-dest", dest);
        metaStore.insertFileDiff(fileDiff);
      } else {
        insertDeleteDiff(src, info.isdir());
      }
    } else if (inBackup(dest)) {
      // tackle such case: rename file from outside into backup dir
      if (!info.isdir()) {
        FileDiff fileDiff = new FileDiff(FileDiffType.APPEND);
        fileDiff.setSrc(dest);
        fileDiff.getParameters().put("-offset", String.valueOf(0));
        fileDiff.getParameters()
            .put("-length", String.valueOf(info.getLength()));
        metaStore.insertFileDiff(fileDiff);
      } else {
        List<FileInfo> fileInfos = metaStore.getFilesByPrefix(src.endsWith("/") ? src : src + "/");
        for (FileInfo fileInfo : fileInfos) {
          // TODO: cover subdir with no file case
          if (fileInfo.isdir()) {
            continue;
          }
          FileDiff fileDiff = new FileDiff(FileDiffType.APPEND);
          fileDiff.setSrc(fileInfo.getPath().replaceFirst(src, dest));
          fileDiff.getParameters().put("-offset", String.valueOf(0));
          fileDiff.getParameters()
              .put("-length", String.valueOf(fileInfo.getLength()));
          metaStore.insertFileDiff(fileDiff);
        }
      }
    }
    if (status == null) {
      LOG.debug("Get rename dest status failed, {} -> {}", src, dest);
    }
    // The dest path which the src is renamed to should be checked in file table
    // to avoid duplicated record for one same path.
    FileInfo destInfo = metaStore.getFile(dest);
    if (destInfo != null) {
      metaStore.deleteFileByPath(dest);
    }
    // src is not in file table because it is not fetched or other reason
    if (info == null) {
      if (status != null) {
        //info = HadoopUtil.convertFileStatus(status, dest);
        //metaStore.insertFile(info);
        namespaceFetcher.startFetch(dest);
        while(!namespaceFetcher.fetchFinished()) {
          LOG.info("Fetching the files under " + dest);
          Thread.sleep(100);
        }
        namespaceFetcher.stop();
      }
    } else {
      // if the dest is ignored, delete src info from file table
      // TODO: tackle with file_state and small_state
      if (shouldIgnore(dest)) {
        // fuzzy matching is used to delete content under the dir
        if (info.isdir()) {
          ret.add(String.format("DELETE FROM file WHERE path LIKE '%s/%%';", src));
        }
        ret.add(String.format("DELETE FROM file WHERE path = '%s';", src));
        return ret;
      } else {
        ret.add(String.format("UPDATE file SET path = replace(path, '%s', '%s') "
            + "WHERE path = '%s';", src, dest, src));
        ret.add(String.format("UPDATE file_state SET path = replace(path, '%s', '%s') "
            + "WHERE path = '%s';", src, dest, src));
        ret.add(String.format("UPDATE small_file SET path = replace(path, '%s', '%s') "
            + "WHERE path = '%s';", src, dest, src));
        if (info.isdir()) {
          if (metaStore.getDbType() == DBType.MYSQL) {
            ret.add(String.format("UPDATE file SET path = CONCAT('%s', SUBSTR(path, %d)) "
                + "WHERE path LIKE '%s/%%';", dest, src.length() + 1, src));
            ret.add(String.format("UPDATE file_state SET path = CONCAT('%s', SUBSTR(path, %d)) "
                + "WHERE path LIKE '%s/%%';", dest, src.length() + 1, src));
            ret.add(String.format("UPDATE small_file SET path = CONCAT('%s', SUBSTR(path, %d)) "
                + "WHERE path LIKE '%s/%%';", dest, src.length() + 1, src));
          } else if (metaStore.getDbType() == DBType.SQLITE) {
            ret.add(String.format("UPDATE file SET path = '%s' || SUBSTR(path, %d) "
                + "WHERE path LIKE '%s/%%';", dest, src.length() + 1, src));
            ret.add(String.format("UPDATE file_state SET path = '%s' || SUBSTR(path, %d) "
                + "WHERE path LIKE '%s/%%';", dest, src.length() + 1, src));
            ret.add(String.format("UPDATE small_file SET path = '%s' || SUBSTR(path, %d) "
                + "WHERE path LIKE '%s/%%';", dest, src.length() + 1, src));
          }
        }
      }
    }
    return ret;
  }

Answer: private String getMetaDataUpdateSql(Event.MetadataUpdateEvent metadataUpdateEvent) throws MetaStoreException {

<think>
The code just finished the getRenameSql method, which returns a List<String>. The switch statement earlier calls getMetaDataUpdateSql, getAppendSql, and getUnlinkSql, so the next method to define is getMetaDataUpdateSql. Its signature should match the usage: it returns a String and takes an Event.MetadataUpdateEvent, throwing IOException and MetaStoreException. Therefore the next line is the method declaration.
</think>
private String getMetaDataUpdateSql(Event.MetadataUpdateEvent metadataEvent) throws IOException, MetaStoreException {


Context:
// Copyright 2011 The Noda Time Authors. All rights reserved.
// Use of this source code is governed by the Apache License 2.0,
// as found in the LICENSE.txt file.

using System.Collections.Generic;
using System.Linq;
using NodaTime.Text;
using NUnit.Framework;
using System.Globalization;

namespace NodaTime.Test.Text
{
    public class OffsetPatternTest : PatternTestBase<Offset>
    {
        /// <summary>
        /// A non-breaking space.
        /// </summary>
        public const string Nbsp = "\u00a0";

        /// <summary>
        /// Test data that can only be used to test formatting.
        /// </summary>
        internal static readonly Data[] FormatOnlyData = {
            new Data(3, 0, 0) { Culture = Cultures.EnUs, Text = "", Pattern = "%-", },

            // Losing information
            new Data(5, 6, 7) { Culture = Cultures.EnUs, Text = "05", Pattern = "HH" },
            new Data(5, 6, 7) { Culture = Cultures.EnUs, Text = "06", Pattern = "mm" },
            new Data(5, 6, 7) { Culture = Cultures.EnUs, Text = "07", Pattern = "ss" },
            new Data(5, 6, 7) { Culture = Cultures.EnUs, Text = "5", Pattern = "%H" },
            new Data(5, 6, 7) { Culture = Cultures.EnUs, Text = "6", Pattern = "%m" },
            new Data(5, 6, 7) { Culture = Cultures.EnUs, Text = "7", Pattern = "%s" },

            new Data(Offset.MaxValue) { Culture = Cultures.EnUs, Text = "+18", Pattern = "g" },
            new Data(Offset.MaxValue) { Culture = Cultures.EnUs, Text = "18", Pattern = "%H" },
            new Data(Offset.MaxValue) { Culture = Cultures.EnUs, Text = "0", Pattern = "%m" },
            new Data(Offset.MaxValue) { Culture = Cultures.EnUs, Text = "0", Pattern = "%s" },
            new Data(Offset.MaxValue) { Culture = Cultures.EnUs, Text = "m", Pattern = "\\m" },
            new Data(Offset.MaxValue) { Culture = Cultures.EnUs, Text = "m", Pattern = "'m'" },
            new Data(Offset.MaxValue) { Culture = Cultures.EnUs, Text = "mmmmmmmmmm", Pattern = "'mmmmmmmmmm'" },
            new Data(Offset.MaxValue) { Culture = Cultures.EnUs, Text = "z", Pattern = "'z'" },
            new Data(Offset.MaxValue) { Culture = Cultures.EnUs, Text = "zqw", Pattern = "'zqw'" },
            new Data(3, 0, 0, true) { Culture = Cultures.EnUs, Text = "-", Pattern = "%-" },
            new Data(3, 0, 0) { Culture = Cultures.EnUs, Text = "+", Pattern = "%+" },
            new Data(3, 0, 0, true) { Culture = Cultures.EnUs, Text = "-", Pattern = "%+" },
            new Data(5, 12, 34) { Culture = Cultures.EnUs, Text = "+05", Pattern = "s" },
            new Data(5, 12, 34) { Culture = Cultures.EnUs, Text = "+05:12", Pattern = "m" },
            new Data(5, 12, 34) { Culture = Cultures.EnUs, Text = "+05:12:34", Pattern = "l" },
        };

        /// <summary>
        /// Test data that can only be used to test successful parsing.
        /// </summary>
        internal static readonly Data[] ParseOnlyData = {
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "*", Pattern = "%*" },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "zqw", Pattern = "'zqw'" },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "-", Pattern = "%-" },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "+", Pattern = "%+" },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "-", Pattern = "%+" },
            new Data(5, 0, 0) { Culture = Cultures.EnUs, Text = "+05", Pattern = "s" },
            new Data(5, 12, 0) { Culture = Cultures.EnUs, Text = "+05:12", Pattern = "m" },
            new Data(5, 12, 34) { Culture = Cultures.EnUs, Text = "+05:12:34", Pattern = "l" },
            new Data(Offset.Zero) { Pattern = "Z+HH:mm", Text = "+00:00" } // Lenient when parsing Z-prefixed patterns.
        };

        /// <summary>
        /// Test data for invalid patterns
        /// </summary>
        internal static readonly Data[] InvalidPatternData = {
            new Data(Offset.Zero) { Pattern = "", Message = TextErrorMessages.FormatStringEmpty },
            new Data(Offset.Zero) { Pattern = "%Z", Message = TextErrorMessages.EmptyZPrefixedOffsetPattern },
            new Data(Offset.Zero) { Pattern = "HH:mmZ", Message = TextErrorMessages.ZPrefixNotAtStartOfPattern },
            new Data(Offset.Zero) { Pattern = "%%H", Message = TextErrorMessages.PercentDoubled},
            new Data(Offset.Zero) { Pattern = "HH:HH", Message = TextErrorMessages.RepeatedFieldInPattern, Parameters = {'H'} },
            new Data(Offset.Zero) { Pattern = "mm:mm", Message = TextErrorMessages.RepeatedFieldInPattern, Parameters = {'m'} },
            new Data(Offset.Zero) { Pattern = "ss:ss", Message = TextErrorMessages.RepeatedFieldInPattern, Parameters = {'s'} },
            new Data(Offset.Zero) { Pattern = "+HH:-mm", Message = TextErrorMessages.RepeatedFieldInPattern, Parameters = {'-'} },
            new Data(Offset.Zero) { Pattern = "-HH:+mm", Message = TextErrorMessages.RepeatedFieldInPattern, Parameters = {'+'} },
            new Data(Offset.Zero) { Pattern = "!", Message = TextErrorMessages.UnknownStandardFormat, Parameters = {'!', typeof(Offset).FullName}},
            new Data(Offset.Zero) { Pattern = "%", Message = TextErrorMessages.UnknownStandardFormat, Parameters = { '%', typeof(Offset).FullName } },
            new Data(Offset.Zero) { Pattern = "%%", Message = TextErrorMessages.PercentDoubled },
            new Data(Offset.Zero) { Pattern = "%\\", Message = TextErrorMessages.EscapeAtEndOfString },
            new Data(Offset.Zero) { Pattern = "\\", Message = TextErrorMessages.UnknownStandardFormat, Parameters = { '\\', typeof(Offset).FullName } },
            new Data(Offset.Zero) { Pattern = "H%", Message = TextErrorMessages.PercentAtEndOfString },
            new Data(Offset.Zero) { Pattern = "hh", Message = TextErrorMessages.Hour12PatternNotSupported, Parameters = { typeof(Offset).FullName } },
            new Data(Offset.Zero) { Pattern = "HHH", Message = TextErrorMessages.RepeatCountExceeded, Parameters = { 'H', 2 } },
            new Data(Offset.Zero) { Pattern = "mmm", Message = TextErrorMessages.RepeatCountExceeded, Parameters = { 'm', 2 } },
            new Data(Offset.Zero) { Pattern = "mmmmmmmmmmmmmmmmmmm", Message = TextErrorMessages.RepeatCountExceeded, Parameters = { 'm', 2 } },
            new Data(Offset.Zero) { Pattern = "'qwe", Message = TextErrorMessages.MissingEndQuote, Parameters = { '\'' } },
            new Data(Offset.Zero) { Pattern = "'qwe\\", Message = TextErrorMessages.EscapeAtEndOfString },
            new Data(Offset.Zero) { Pattern = "'qwe\\'", Message = TextErrorMessages.MissingEndQuote, Parameters = { '\'' } },
            new Data(Offset.Zero) { Pattern = "sss", Message = TextErrorMessages.RepeatCountExceeded, Parameters = { 's', 2 } },
        };

        /// <summary>
        /// Tests for parsing failures (of values)
        /// </summary>
        internal static readonly Data[] ParseFailureData = {
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "", Pattern = "g", Message = TextErrorMessages.ValueStringEmpty },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "1", Pattern = "HH", Message = TextErrorMessages.MismatchedNumber, Parameters = {"HH"} },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "1", Pattern = "mm", Message = TextErrorMessages.MismatchedNumber, Parameters = {"mm"} },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "1", Pattern = "ss", Message = TextErrorMessages.MismatchedNumber, Parameters = {"ss"} },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "12:34 ", Pattern = "HH:mm", Message = TextErrorMessages.ExtraValueCharacters, Parameters = {" "} },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "1a", Pattern = "H ", Message = TextErrorMessages.MismatchedCharacter, Parameters = {' '}},
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "2:", Pattern = "%H", Message = TextErrorMessages.ExtraValueCharacters, Parameters = {":"}},
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "a", Pattern = "%.", Message = TextErrorMessages.MismatchedCharacter, Parameters = {'.'}},
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "a", Pattern = "%:", Message = TextErrorMessages.TimeSeparatorMismatch},
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "a", Pattern = "%H", Message = TextErrorMessages.MismatchedNumber, Parameters = {"H"} },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "a", Pattern = "%m", Message = TextErrorMessages.MismatchedNumber, Parameters = {"m"} },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "a", Pattern = "%s", Message = TextErrorMessages.MismatchedNumber, Parameters = {"s"} },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "a", Pattern = ".H", Message = TextErrorMessages.MismatchedCharacter, Parameters = {'.'}},
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "a", Pattern = "\\'", Message = TextErrorMessages.EscapedCharacterMismatch, Parameters = {'\''} },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "axc", Pattern = "'abc'", Message = TextErrorMessages.QuotedStringMismatch},
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "z", Pattern = "%*", Message = TextErrorMessages.MismatchedCharacter, Parameters = {'*'} },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "24", Pattern = "HH", Message = TextErrorMessages.FieldValueOutOfRange, Parameters = {24, 'H', typeof(Offset) }},
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "60", Pattern = "mm", Message = TextErrorMessages.FieldValueOutOfRange, Parameters = {60, 'm', typeof(Offset) }},
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "60", Pattern = "ss", Message = TextErrorMessages.FieldValueOutOfRange, Parameters = {60, 's', typeof(Offset) }},
            new Data(Offset.Zero) { Text = "+12", Pattern = "-HH", Message = TextErrorMessages.PositiveSignInvalid },
        };

        /// <summary>
        /// Common test data for both formatting and parsing. A test should be placed here unless is truly
        /// cannot be run both ways. This ensures that as many round-trip type tests are performed as possible.
        /// </summary>
        internal static readonly Data[] FormatAndParseData = {
/*XXX*/            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = ".", Pattern = "%." }, // decimal separator
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = ":", Pattern = "%:" }, // date separator
/*XXX*/            new Data(Offset.Zero) { Culture = Cultures.DotTimeSeparator, Text = ".", Pattern = "%." }, // decimal separator (always period)
            new Data(Offset.Zero) { Culture = Cultures.DotTimeSeparator, Text = ".", Pattern = "%:" }, // date separator
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "H", Pattern = "\\H" },
            new Data(Offset.Zero) { Culture = Cultures.EnUs, Text = "HHss", Pattern = "'HHss'" },
            new Data(0, 0, 12) { Culture = Cultures.EnUs, Text = "12", Pattern = "%s" },
            new Data(0, 0, 12) { Culture = Cultures.EnUs, Text = "12", Pattern = "ss" },
            new Data(0, 0, 2) { Culture = Cultures.EnUs, Text = "2", Pattern = "%s" },
            new Data(0, 12, 0) { Culture = Cultures.EnUs, Text = "12", Pattern = "%m" },
            new Data(0, 12, 0) { Culture = Cultures.EnUs, Text = "12", Pattern = "mm" },
            new Data(0, 2, 0) { Culture = Cultures.EnUs, Text = "2", Pattern = "%m" },

            new Data(12, 0, 0) { Culture = Cultures.EnUs, Text = "12", Pattern = "%H" },
            new Data(12, 0, 0) { Culture = Cultures.EnUs, Text = "12", Pattern = "HH" },
            new Data(2, 0, 0) { Culture = Cultures.EnUs, Text = "2", Pattern = "%H" },

            // Standard patterns with punctuation...
            new Data(5, 0, 0) { Culture = Cultures.EnUs, Text = "+05", Pattern = "G"  },
            new Data(5, 12, 0) { Culture = Cultures.EnUs, Text = "+05:12", Pattern = "G"  },

Answer: new Data(5, 12, 34) { Culture = Cultures.EnUs, Text = "+05:12:34", Pattern = "G"  },

<think>
We are inside the static array FormatAndParseData. The last entry shown is the G pattern for hours and minutes. The next logical entry continues testing the same pattern with seconds included, matching the earlier test data. So the next line adds a Data instance with 5 hours, 12 minutes, 34 seconds, same culture, text "+05:12:34", and Pattern = "G".
</think>
new Data(5, 12, 34) { Culture = Cultures.EnUs, Text = "+05:12:34", Pattern = "G" },


Context:
//    Openbravo POS is a point of sales application designed for touch screens.
//    Copyright (C) 2007-2009 Openbravo, S.L.
//    http://www.openbravo.com/product/pos
//
//    This file is part of Openbravo POS.
//
//    Openbravo POS is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    Openbravo POS is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Openbravo POS.  If not, see <http://www.gnu.org/licenses/>.

package com.openbravo.pos.customers;

import java.awt.Component;
import java.util.List;

import javax.swing.JOptionPane;

import com.openbravo.basic.BasicException;
import com.openbravo.data.gui.ComboBoxValModel;
import com.openbravo.data.loader.SentenceList;
import com.openbravo.data.user.DirtyManager;
import com.openbravo.data.user.EditorRecord;
import com.openbravo.format.Formats;
import com.openbravo.pos.forms.AppLocal;
import com.openbravo.pos.forms.AppView;
import com.openbravo.pos.forms.DataLogicSales;
import com.openbravo.pos.util.StringUtils;

/**
 *
 * @author  adrianromero
 */
public class CustomersView extends javax.swing.JPanel implements EditorRecord {

    private Long m_oId;

    private SentenceList m_sentcat;
    private ComboBoxValModel m_CategoryModel;

    private DataLogicCustomers dlCustomers;
    private DirtyManager m_Dirty;

    /** Creates new form CustomersView
     * @param dlCustomers */
    public CustomersView(AppView app, DataLogicCustomers dlCustomers, DirtyManager dirty) {
        this.dlCustomers = dlCustomers;
        DataLogicSales dlSales = (DataLogicSales) app.getBean("com.openbravo.pos.forms.DataLogicSales");

        initComponents();

        m_sentcat = dlSales.getTaxCustCategoriesList();
        m_CategoryModel = new ComboBoxValModel();

        m_Dirty = dirty;
        m_jTaxID.getDocument().addDocumentListener(dirty);
        m_jSearchkey.getDocument().addDocumentListener(dirty);
        m_jName.getDocument().addDocumentListener(dirty);
        m_jCategory.addActionListener(dirty);
        m_jNotes.getDocument().addDocumentListener(dirty);
        txtMaxdebt.getDocument().addDocumentListener(dirty);
        m_jVisible.addActionListener(dirty);

        txtFirstName.getDocument().addDocumentListener(dirty);
        txtLastName.getDocument().addDocumentListener(dirty);
        txtEmail.getDocument().addDocumentListener(dirty);
        txtPhone.getDocument().addDocumentListener(dirty);
        txtPhone2.getDocument().addDocumentListener(dirty);
        txtFax.getDocument().addDocumentListener(dirty);

        txtAddress.getDocument().addDocumentListener(dirty);
        txtAddress2.getDocument().addDocumentListener(dirty);
        txtPostal.getDocument().addDocumentListener(dirty);
        txtCity.getDocument().addDocumentListener(dirty);
        txtRegion.getDocument().addDocumentListener(dirty);
        txtCountry.getDocument().addDocumentListener(dirty);

        writeValueEOF();
    }

    public void activate() throws BasicException {

        List a = m_sentcat.list();
        a.add(0, null); // The null item
        m_CategoryModel = new ComboBoxValModel(a);
        m_jCategory.setModel(m_CategoryModel);
    }

    public void refresh() {
    }

    public void writeValueEOF() {
        m_oId = null;
        m_jTaxID.setText(null);
        m_jSearchkey.setText(null);
        m_jName.setText(null);
        m_CategoryModel.setSelectedKey(null);
        m_jNotes.setText(null);
        txtMaxdebt.setText(null);
        txtCurdebt.setText(null);
        txtCurdate.setText(null);
        m_jVisible.setSelected(false);
        jcard.setText(null);

        txtFirstName.setText(null);
        txtLastName.setText(null);
        txtEmail.setText(null);
        txtPhone.setText(null);
        txtPhone2.setText(null);
        txtFax.setText(null);

        txtAddress.setText(null);
        txtAddress2.setText(null);
        txtPostal.setText(null);
        txtCity.setText(null);
        txtRegion.setText(null);
        txtCountry.setText(null);

        m_jTaxID.setEnabled(false);
        m_jSearchkey.setEnabled(false);
        m_jName.setEnabled(false);
        m_jCategory.setEnabled(false);
        m_jNotes.setEnabled(false);
        txtMaxdebt.setEnabled(false);
        txtCurdebt.setEnabled(false);
        txtCurdate.setEnabled(false);
        m_jVisible.setEnabled(false);
        jcard.setEnabled(false);

        txtFirstName.setEnabled(false);
        txtLastName.setEnabled(false);
        txtEmail.setEnabled(false);
        txtPhone.setEnabled(false);
        txtPhone2.setEnabled(false);
        txtFax.setEnabled(false);

        txtAddress.setEnabled(false);
        txtAddress2.setEnabled(false);
        txtPostal.setEnabled(false);
        txtCity.setEnabled(false);
        txtRegion.setEnabled(false);
        txtCountry.setEnabled(false);

        jButton2.setEnabled(false);
        jButton3.setEnabled(false);
    }

    public void writeValueInsert() {
        m_oId = null;
        m_jTaxID.setText(null);
        m_jSearchkey.setText(null);
        m_jName.setText(null);
        m_CategoryModel.setSelectedKey(null);
        m_jNotes.setText(null);
        txtMaxdebt.setText(null);
        txtCurdebt.setText(null);
        txtCurdate.setText(null);
        m_jVisible.setSelected(true);
        jcard.setText(null);

        txtFirstName.setText(null);
        txtLastName.setText(null);
        txtEmail.setText(null);
        txtPhone.setText(null);
        txtPhone2.setText(null);
        txtFax.setText(null);

        txtAddress.setText(null);
        txtAddress2.setText(null);
        txtPostal.setText(null);
        txtCity.setText(null);
        txtRegion.setText(null);
        txtCountry.setText(null);

        m_jTaxID.setEnabled(true);
        m_jSearchkey.setEnabled(true);
        m_jName.setEnabled(true);
        m_jCategory.setEnabled(true);
        m_jNotes.setEnabled(true);
        txtMaxdebt.setEnabled(true);
        txtCurdebt.setEnabled(true);
        txtCurdate.setEnabled(true);
        m_jVisible.setEnabled(true);
        jcard.setEnabled(true);

        txtFirstName.setEnabled(true);
        txtLastName.setEnabled(true);
        txtEmail.setEnabled(true);
        txtPhone.setEnabled(true);
        txtPhone2.setEnabled(true);
        txtFax.setEnabled(true);

        txtAddress.setEnabled(true);
        txtAddress2.setEnabled(true);
        txtPostal.setEnabled(true);
        txtCity.setEnabled(true);
        txtRegion.setEnabled(true);
        txtCountry.setEnabled(true);

        jButton2.setEnabled(true);
        jButton3.setEnabled(true);
    }

    public void writeValueDelete(Object value) {
        Object[] customer = (Object[]) value;
        m_oId = (Long) customer[0];
        m_jTaxID.setText((String) customer[1]);
        m_jSearchkey.setText((String) customer[2]);
        m_jName.setText((String) customer[3]);
        m_jNotes.setText((String) customer[4]);
        m_jVisible.setSelected(((Boolean) customer[5]).booleanValue());
        jcard.setText((String) customer[6]);
        txtMaxdebt.setText(Formats.CURRENCY.formatValue(customer[7]));
        txtCurdate.setText(Formats.DATE.formatValue(customer[8]));
        txtCurdebt.setText(Formats.CURRENCY.formatValue(customer[9]));

        txtFirstName.setText(Formats.STRING.formatValue(customer[10]));
        txtLastName.setText(Formats.STRING.formatValue(customer[11]));
        txtEmail.setText(Formats.STRING.formatValue(customer[12]));
        txtPhone.setText(Formats.STRING.formatValue(customer[13]));
        txtPhone2.setText(Formats.STRING.formatValue(customer[14]));
        txtFax.setText(Formats.STRING.formatValue(customer[15]));

        txtAddress.setText(Formats.STRING.formatValue(customer[16]));
        txtAddress2.setText(Formats.STRING.formatValue(customer[17]));
        txtPostal.setText(Formats.STRING.formatValue(customer[18]));
        txtCity.setText(Formats.STRING.formatValue(customer[19]));
        txtRegion.setText(Formats.STRING.formatValue(customer[20]));
        txtCountry.setText(Formats.STRING.formatValue(customer[21]));

        m_CategoryModel.setSelectedKey(customer[22]);

        m_jTaxID.setEnabled(false);
        m_jSearchkey.setEnabled(false);
        m_jName.setEnabled(false);
        m_jNotes.setEnabled(false);
        txtMaxdebt.setEnabled(false);
        txtCurdebt.setEnabled(false);
        txtCurdate.setEnabled(false);
        m_jVisible.setEnabled(false);
        jcard.setEnabled(false);

        txtFirstName.setEnabled(false);
        txtLastName.setEnabled(false);
        txtEmail.setEnabled(false);
        txtPhone.setEnabled(false);
        txtPhone2.setEnabled(false);
        txtFax.setEnabled(false);

        txtAddress.setEnabled(false);
        txtAddress2.setEnabled(false);
        txtPostal.setEnabled(false);
        txtCity.setEnabled(false);
        txtRegion.setEnabled(false);
        txtCountry.setEnabled(false);

        m_jCategory.setEnabled(false);

        jButton2.setEnabled(false);
        jButton3.setEnabled(false);
    }

    public void writeValueEdit(Object value) {
        Object[] customer = (Object[]) value;
        m_oId = (Long) customer[0];
        m_jTaxID.setText((String) customer[1]);
        m_jSearchkey.setText((String) customer[2]);
        m_jName.setText((String) customer[3]);
        m_jNotes.setText((String) customer[4]);
        m_jVisible.setSelected(((Boolean) customer[5]).booleanValue());
        jcard.setText((String) customer[6]);
        txtMaxdebt.setText(Formats.CURRENCY.formatValue(customer[7]));
        txtCurdate.setText(Formats.DATE.formatValue(customer[8]));
        txtCurdebt.setText(Formats.CURRENCY.formatValue(customer[9]));

        txtFirstName.setText(Formats.STRING.formatValue(customer[10]));
        txtLastName.setText(Formats.STRING.formatValue(customer[11]));
        txtEmail.setText(Formats.STRING.formatValue(customer[12]));
        txtPhone.setText(Formats.STRING.formatValue(customer[13]));
        txtPhone2.setText(Formats.STRING.formatValue(customer[14]));
        txtFax.setText(Formats.STRING.formatValue(customer[15]));

        txtAddress.setText(Formats.STRING.formatValue(customer[16]));
        txtAddress2.setText(Formats.STRING.formatValue(customer[17]));
        txtPostal.setText(Formats.STRING.formatValue(customer[18]));
        txtCity.setText(Formats.STRING.formatValue(customer[19]));
        txtRegion.setText(Formats.STRING.formatValue(customer[20]));
        txtCountry.setText(Formats.STRING.formatValue(customer[21]));

        m_CategoryModel.setSelectedKey(customer[22]);

        m_jTaxID.setEnabled(true);
        m_jSearchkey.setEnabled(true);
        m_jName.setEnabled(true);
        m_jNotes.setEnabled(true);
        txtMaxdebt.setEnabled(true);
        txtCurdebt.setEnabled(true);
        txtCurdate.setEnabled(true);
        m_jVisible.setEnabled(true);
        jcard.setEnabled(true);

        txtFirstName.setEnabled(true);
        txtLastName.setEnabled(true);
        txtEmail.setEnabled(true);
        txtPhone.setEnabled(true);
        txtPhone2.setEnabled(true);
        txtFax.setEnabled(true);

        txtAddress.setEnabled(true);
        txtAddress2.setEnabled(true);
        txtPostal.setEnabled(true);
        txtCity.setEnabled(true);
        txtRegion.setEnabled(true);
        txtCountry.setEnabled(true);

        m_jCategory.setEnabled(true);

        jButton2.setEnabled(true);
        jButton3.setEnabled(true);
    }

    public Object createValue() throws BasicException {
        Object[] customer = new Object[23];
        customer[0] = m_oId == null ? dlCustomers.getNextId(CustomerInfo.class) : m_oId;
        customer[1] = m_jTaxID.getText();
        customer[2] = m_jSearchkey.getText();
        customer[3] = m_jName.getText();
        customer[4] = m_jNotes.getText();
        customer[5] = Boolean.valueOf(m_jVisible.isSelected());
        customer[6] = Formats.STRING.parseValue(jcard.getText()); // Format to manage NULL values
        customer[7] = Formats.CURRENCY.parseValue(txtMaxdebt.getText(), new Double(0.0));
        customer[8] = Formats.TIMESTAMP.parseValue(txtCurdate.getText()); // not saved
        customer[9] = Formats.CURRENCY.parseValue(txtCurdebt.getText()); // not saved

        customer[10] = Formats.STRING.parseValue(txtFirstName.getText());
        customer[11] = Formats.STRING.parseValue(txtLastName.getText());
        customer[12] = Formats.STRING.parseValue(txtEmail.getText());
        customer[13] = Formats.STRING.parseValue(txtPhone.getText());
        customer[14] = Formats.STRING.parseValue(txtPhone2.getText());
        customer[15] = Formats.STRING.parseValue(txtFax.getText());

        customer[16] = Formats.STRING.parseValue(txtAddress.getText());
        customer[17] = Formats.STRING.parseValue(txtAddress2.getText());
        customer[18] = Formats.STRING.parseValue(txtPostal.getText());
        customer[19] = Formats.STRING.parseValue(txtCity.getText());
        customer[20] = Formats.STRING.parseValue(txtRegion.getText());
        customer[21] = Formats.STRING.parseValue(txtCountry.getText());

        customer[22] = m_CategoryModel.getSelectedKey();

        return customer;
    }

    public Component getComponent() {
        return this;
    }
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jLabel7 = new javax.swing.JLabel();
        m_jTaxID = new javax.swing.JTextField();
        jLabel8 = new javax.swing.JLabel();
        m_jSearchkey = new javax.swing.JTextField();
        jLabel3 = new javax.swing.JLabel();
        m_jName = new javax.swing.JTextField();
        jLabel4 = new javax.swing.JLabel();
        m_jVisible = new javax.swing.JCheckBox();
        jLabel5 = new javax.swing.JLabel();
        jcard = new javax.swing.JTextField();
        jLabel9 = new javax.swing.JLabel();
        m_jCategory = new javax.swing.JComboBox();
        jButton2 = new javax.swing.JButton();
        jButton3 = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        txtMaxdebt = new javax.swing.JTextField();
        jTabbedPane1 = new javax.swing.JTabbedPane();
        jPanel1 = new javax.swing.JPanel();
        jLabel14 = new javax.swing.JLabel();
        txtFax = new javax.swing.JTextField();
        jLabel15 = new javax.swing.JLabel();
        txtLastName = new javax.swing.JTextField();
        jLabel16 = new javax.swing.JLabel();
        txtEmail = new javax.swing.JTextField();
        jLabel17 = new javax.swing.JLabel();
        txtPhone = new javax.swing.JTextField();
        jLabel18 = new javax.swing.JLabel();
        txtPhone2 = new javax.swing.JTextField();
        jLabel19 = new javax.swing.JLabel();
        txtFirstName = new javax.swing.JTextField();
        jPanel2 = new javax.swing.JPanel();
        jLabel13 = new javax.swing.JLabel();
        txtAddress = new javax.swing.JTextField();
        jLabel20 = new javax.swing.JLabel();
        txtCountry = new javax.swing.JTextField();
        jLabel21 = new javax.swing.JLabel();
        txtAddress2 = new javax.swing.JTextField();
        jLabel22 = new javax.swing.JLabel();
        txtPostal = new javax.swing.JTextField();
        jLabel23 = new javax.swing.JLabel();
        txtCity = new javax.swing.JTextField();
        jLabel24 = new javax.swing.JLabel();
        txtRegion = new javax.swing.JTextField();
        jPanel3 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        m_jNotes = new javax.swing.JTextArea();
        jLabel2 = new javax.swing.JLabel();
        txtCurdebt = new javax.swing.JTextField();
        jLabel6 = new javax.swing.JLabel();
        txtCurdate = new javax.swing.JTextField();

        jLabel7.setText(AppLocal.getIntString("label.taxid")); // NOI18N

        jLabel8.setText(AppLocal.getIntString("label.searchkey")); // NOI18N

        jLabel3.setText(AppLocal.getIntString("label.name")); // NOI18N

        jLabel4.setText(AppLocal.getIntString("label.visible")); // NOI18N

        jLabel5.setText(AppLocal.getIntString("label.card")); // NOI18N

        jcard.setEditable(false);

        jLabel9.setText(AppLocal.getIntString("label.custtaxcategory")); // NOI18N

        jButton2.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/openbravo/images/color_line16.png"))); // NOI18N
        jButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });

        jButton3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/com/openbravo/images/fileclose.png"))); // NOI18N
        jButton3.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton3ActionPerformed(evt);
            }
        });

        jLabel1.setText(AppLocal.getIntString("label.maxdebt")); // NOI18N

        txtMaxdebt.setHorizontalAlignment(javax.swing.JTextField.RIGHT);

        jLabel14.setText(AppLocal.getIntString("label.fax")); // NOI18N

        jLabel15.setText(AppLocal.getIntString("label.lastname")); // NOI18N

        jLabel16.setText(AppLocal.getIntString("label.email")); // NOI18N

        jLabel17.setText(AppLocal.getIntString("label.phone")); // NOI18N

        jLabel18.setText(AppLocal.getIntString("label.phone2")); // NOI18N

        jLabel19.setText(AppLocal.getIntString("label.firstname")); // NOI18N

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(jLabel19, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtFirstName, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(jLabel15, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtLastName, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(jLabel16, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtEmail, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(jLabel17, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtPhone, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(jLabel18, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtPhone2, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(jLabel14, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtFax, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(249, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel19)
                    .addComponent(txtFirstName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel15)
                    .addComponent(txtLastName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel16)
                    .addComponent(txtEmail, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel17)
                    .addComponent(txtPhone, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel18)
                    .addComponent(txtPhone2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel14)
                    .addComponent(txtFax, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        jTabbedPane1.addTab(AppLocal.getIntString("label.contact"), jPanel1); // NOI18N

        jLabel13.setText(AppLocal.getIntString("label.address")); // NOI18N

        jLabel20.setText(AppLocal.getIntString("label.country")); // NOI18N

        jLabel21.setText(AppLocal.getIntString("label.address2")); // NOI18N

        jLabel22.setText(AppLocal.getIntString("label.postal")); // NOI18N

        jLabel23.setText(AppLocal.getIntString("label.city")); // NOI18N

        jLabel24.setText(AppLocal.getIntString("label.region")); // NOI18N

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel13, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtAddress, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel21, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtAddress2, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel22, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtPostal, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel23, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtCity, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel24, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtRegion, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel20, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtCountry, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(249, Short.MAX_VALUE))
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel13)
                    .addComponent(txtAddress, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel21)
                    .addComponent(txtAddress2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel22)
                    .addComponent(txtPostal, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel23)
                    .addComponent(txtCity, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel24)
                    .addComponent(txtRegion, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel20)
                    .addComponent(txtCountry, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(267, 267, 267))
        );

        jTabbedPane1.addTab(AppLocal.getIntString("label.location"), jPanel2); // NOI18N

        jScrollPane1.setViewportView(m_jNotes);

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 659, Short.MAX_VALUE)
                .addContainerGap())
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 144, Short.MAX_VALUE)
                .addContainerGap())
        );

        jTabbedPane1.addTab(AppLocal.getIntString("label.notes"), jPanel3); // NOI18N

        jLabel2.setText(AppLocal.getIntString("label.curdebt")); // NOI18N

        txtCurdebt.setEditable(false);
        txtCurdebt.setHorizontalAlignment(javax.swing.JTextField.RIGHT);

        jLabel6.setText(AppLocal.getIntString("label.curdate")); // NOI18N

        txtCurdate.setEditable(false);
        txtCurdate.setHorizontalAlignment(javax.swing.JTextField.CENTER);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 688, Short.MAX_VALUE)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(jLabel7, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(m_jTaxID, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(jLabel8, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(m_jSearchkey, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(jLabel3, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(m_jName, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(jLabel4, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(m_jVisible, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addGap(246, 246, 246))
                        .addGroup(layout.createSequentialGroup()
                            .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                .addGroup(layout.createSequentialGroup()
                                    .addComponent(jLabel5, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                    .addComponent(jcard, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addGroup(layout.createSequentialGroup()
                                    .addComponent(jLabel9, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addGap(12, 12, 12)
                                    .addComponent(m_jCategory, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE)))
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jButton2)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                            .addComponent(jButton3)))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(txtMaxdebt, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 180, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)

Answer: .addComponent(txtCurdebt, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE))

<think>
The layout code is defining a horizontal group for the label jLabel2 (current debt) and then adds a gap. The next logical component is the txtCurdebt field, matching the pattern used earlier for jLabel1 and txtMaxdebt. Therefore the next line adds txtCurdebt with a preferred size.
</think>
.addComponent(txtCurdebt, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)