/*
 * 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 com.sun.jini.logging;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.logging.Level;

/**
 * Defines additional {@link Level} values. <p>
 *
 * See the {@link LogManager} class for one way to use the <code>FAILED</code>
 * and <code>HANDLED</code> logging levels in standard logging configuration
 * files.
 *
 * 
 * @since 2.0
 */
public class Levels {

    /**
     * <code>FAILED</code> is a message level indicating that a facility has
     * experienced a failure that it will reflect to its caller. <p>
     *
     * <code>FAILED</code> messages are intended to provide users with
     * information about failures produced by internal components in order to
     * assist with debugging problems in systems with multiple components. This
     * level is initialized to <code>600</code>.
     */
    public static final Level FAILED = createLevel("FAILED", 600, null);

    /**
     * <code>HANDLED</code> is a message level indicating that a facility has
     * detected a failure that it will take steps to handle without reflecting
     * the failure to its caller. <p>
     *
     * <code>HANDLED</code> messages are intended to provide users with
     * information about failures detected by internal components in order to
     * assist with debugging problems in systems with multiple components. This
     * level is initialized to <code>550</code>.
     */
    public static final Level HANDLED = createLevel("HANDLED", 550, null);

    /** This class cannot be instantiated. */
    private Levels() {
	throw new AssertionError("This class cannot be instantiated");
    }

    /**
     * Defines a class that has the same data format as the Level class, to
     * permit creating the serialized form of a Level instance.
     */
    private static final class LevelData implements Serializable {
	private static final long serialVersionUID = -8176160795706313070L;
	private final String name;
	private final int value;
	private final String resourceBundleName;
	LevelData(String name, int value, String resourceBundleName) {
	    this.name = name;
	    this.value = value;
	    this.resourceBundleName = resourceBundleName;
	}
    }

    /**
     * Defines an object output stream that allows the data for one class to be
     * interpreted as the data for another class.  This class is useful in
     * creating serialization data for a class when access to an appropriate
     * constructor is not available.
     */
    private static final class ClassReplacingObjectOutputStream
	extends ObjectOutputStream
    {
	private final ObjectStreamClass from;
	private final ObjectStreamClass to;

	ClassReplacingObjectOutputStream(OutputStream out,
					 Class from,
					 Class to)
	    throws IOException
	{
	    super(out);
	    this.from = ObjectStreamClass.lookup(from);
	    this.to = ObjectStreamClass.lookup(to);
	}

	protected void writeClassDescriptor(ObjectStreamClass desc)
	    throws IOException
	{
	    if (from.equals(desc)) {
		desc = to;
	    }
	    super.writeClassDescriptor(desc);
	}
    }

    /**
     * Creates an instance of the Level class.  This method works around the
     * fact that there is no public constructor for the Level class by
     * constructing the serialized form for an instance with the specified
     * field values and deserializing it.
     */
    private static Level createLevel(String name,
				     int value,
				     String resourceBundleName)
    {
	try {
	    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
	    ObjectOutputStream out =
		new ClassReplacingObjectOutputStream(
		    bytes, LevelData.class, Level.class);
	    out.writeObject(new LevelData(name, value, resourceBundleName));
	    out.close();
	    ObjectInputStream in = new ObjectInputStream(
		new ByteArrayInputStream(bytes.toByteArray()));
	    Level result = (Level) in.readObject();
	    in.close();
	    return result;
	} catch (Exception e) {
	    throw new RuntimeException("Unexpected exception", e);
	}
    }
}

@@ -1880,30 +1880,34 @@
 el FAILED = 
-create
+new Custom
 Level(%22FAILE
@@ -2460,22 +2460,26 @@
 NDLED = 
-create
+new Custom
 Level(%22H
@@ -2570,16 +2570,20 @@
 ls() %7B%0A%09
+    
 throw ne
@@ -2652,1752 +2652,60 @@
     
-/**%0A     * Defines a class that has the same data format as the Level class, to%0A     * permit creating the serialized form of a Level instance.%0A     */%0A    private static final class LevelData implements Serializable %7B%0A%09private static final long serialVersionUID = -8176160795706313070L;%0A%09private final String name;%0A%09private final int value;%0A%09private final String resourceBundleName;%0A%09LevelData(String name, int value, String resourceBundleName) %7B%0A%09    this.name = name;%0A%09    this.value = value;%0A%09    this.resourceBundleName = resourceBundleName;%0A%09%7D%0A    %7D%0A%0A    /**%0A     * Defines an object output stream that allows the data for one class to be%0A     * interpreted as the data for another class.  This class is useful in%0A     * creating serialization data for a class when access to an appropriate%0A     * constructor is not available.%0A     */%0A    private static final class ClassReplacingObjectOutputStream%0A%09extends ObjectOutputStream%0A    %7B%0A%09private final ObjectStreamClass from;%0A%09private final ObjectStreamClass to;%0A%0A%09ClassReplacingObjectOutputStream(OutputStream out,%0A%09%09%09%09%09 Class from,%0A%09%09%09%09%09 Class to)%0A%09    throws IOException%0A%09%7B%0A%09    super(out);%0A%09    this.from = ObjectStreamClass.lookup(from);%0A%09    this.to = ObjectStreamClass.lookup(to);%0A%09%7D%0A%0A%09protected void writeClassDescriptor(ObjectStreamClass desc)%0A%09    throws IOException%0A%09%7B%0A%09    if (from.equals(desc)) %7B%0A%09%09desc = to;%0A%09    %7D%0A%09    super.writeClassDescriptor(desc);%0A%09%7D%0A    %7D%0A%0A    /**%0A     * Creates an instance of the Level class.  This method works around the%0A     * fact that there is no public constructor for the Level class by%0A     * constructing the serialized form for an instance with the specified%0A     * field values and deserializing it.%0A     */%0A    private static Level create
+static class CustomLevel extends Level %7B%0A%09    Custom
 Leve
@@ -2718,25 +2718,16 @@
 ng name,
-%0A%09%09%09%09    
  int val
@@ -2733,17 +2733,8 @@
 lue,
-%0A%09%09%09%09    
  Str
@@ -2760,234 +2760,25 @@
 ame)
-%0A    %7B%0A%09try %7B%0A%09    ByteArrayOutputStream bytes = new ByteArrayOutputStream();%0A%09    ObjectOutputStream out =%0A%09%09new ClassReplacingObjectOutputStream(%0A%09%09    bytes, LevelData.class, Level.class);%0A%09    out.writeObject(new LevelData
+ %7B%0A%09        super
 (nam
@@ -2810,304 +2810,24 @@
 ame)
-)
 ;%0A%09    
-out.close();%0A%09    ObjectInputStream in = new ObjectInputStream(%0A%09%09new ByteArrayInputStream(bytes.toByteArray()));%0A%09    Level result = (Level) in.readObject();%0A%09    in.close();%0A%09    return result;%0A%09%7D catch (Exception e) %7B%0A%09    throw new RuntimeException(%22Unexpected exception%22, e);%0A%09%7D
+%7D%0A%09%7D%0A
 %0A    
-%7D
 %0A%7D%0A

Fix com.sun.jini.logging.Levels