class IdGenerator {

    public long getNextId() {

        synchronized (syncObject) {
            long timestamp = timeProvider.getTimeInMillis();

            // Deal with the simple case first.
            if (prevTimestamp < timestamp) {
                sequence = 0L;
                prevTimestamp = timestamp;
                return buildKey(timestamp, workerId, sequence);
            }

            // TRICK: If the clock has gone backwards we can still use
            // the sequence counter to generate unique IDs, so we
            // reset the timestamp to prevTimestamp and try our luck
            // with the sequence counter
            timestamp = prevTimestamp;

            // Invariant: we have handed out an ID for this timestamp

            // Increment and wrap
            sequence = ((sequence + 1) & sequenceBitMask);
            if (0L == sequence) {
                // The sequence has wrapped so we cheat and advance
                // the timestamp by 1ms
                timestamp++;
                log.info("Cheating, advancing timestamp by 1ms. workerId = " + workerId);
                prevTimestamp = timestamp;
            }

            return buildKey(timestamp, workerId, sequence);
        }
    }

    public  IdGenerator(final long workerId);
    public  IdGenerator(final long workerId, final TimeProvider timeProvider);
    public  IdGenerator(final long workerId, final int numBitsWorkerId, final int numBitsSequence);
    public  IdGenerator(final long workerId, final TimeProvider timeProvider,
                       final int numBitsWorkerId, final int numBitsSequence);

}